Skip to content
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions .claude/skills/add-model/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,21 @@ Update the CLI dispatch table so `pred` can load, solve, and serialize the new p
- Add a lowercase alias mapping in `resolve_alias()` (e.g., `"newproblem" => "NewProblem".to_string()`)
- Optionally add short aliases to `ALIASES` array (e.g., `("NP", "NewProblem")`)

## Step 4.5: Add CLI creation support

Update `problemreductions-cli/src/commands/create.rs` so `pred create <ProblemName>` works:

1. **Add a match arm** in the `create()` function's main `match canonical.as_str()` block. Parse CLI flags and construct the problem:
- Graph-based problems with vertex weights: add to the `"MaximumIndependentSet" | ... | "MaximalIS"` arm
- Problems with unique fields: add a new arm that parses the required flags and calls the constructor
- See existing arms for patterns (e.g., `"BinPacking"` for simple fields, `"MaximumSetPacking"` for set-based)

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.

3. **Update help text** in `CreateArgs`'s `after_help` to document the new problem's flags.

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.

## Step 5: Write unit tests

Create `src/unit_tests/models/<category>/<name>.rs`:
Expand Down Expand Up @@ -168,3 +183,5 @@ If running standalone (not inside `make run-plan`), invoke [review-implementatio
| Forgetting `declare_variants!` | Required for variant complexity metadata used by the paper's auto-generated table |
| Forgetting CLI dispatch | Must add match arms in `dispatch.rs` (`load_problem` + `serialize_any_problem`) |
| Forgetting CLI alias | Must add lowercase entry in `problem_name.rs` `resolve_alias()` |
| Forgetting CLI create | Must add creation handler in `commands/create.rs` and flags in `cli.rs` |
| Schema lists derived fields | Schema should list constructor params, not internal fields (e.g., `matrix, k` not `matrix, m, n, k`) |
20 changes: 10 additions & 10 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -270,18 +270,18 @@ cli-demo: cli
\
echo ""; \
echo "--- 8. create: build problem instances ---"; \
$$PRED create MIS --edges 0-1,1-2,2-3,3-4,4-0 -o $(CLI_DEMO_DIR)/mis.json; \
$$PRED create MIS --edges 0-1,1-2,2-3 --weights 2,1,3,1 -o $(CLI_DEMO_DIR)/mis_weighted.json; \
$$PRED create MIS --graph 0-1,1-2,2-3,3-4,4-0 -o $(CLI_DEMO_DIR)/mis.json; \
$$PRED create MIS --graph 0-1,1-2,2-3 --weights 2,1,3,1 -o $(CLI_DEMO_DIR)/mis_weighted.json; \
$$PRED create SAT --num-vars 3 --clauses "1,2;-1,3;2,-3" -o $(CLI_DEMO_DIR)/sat.json; \
$$PRED create 3SAT --num-vars 4 --clauses "1,2,3;-1,2,-3;1,-2,3" -o $(CLI_DEMO_DIR)/3sat.json; \
$$PRED create QUBO --matrix "1,-0.5;-0.5,2" -o $(CLI_DEMO_DIR)/qubo.json; \
$$PRED create KColoring --k 3 --edges 0-1,1-2,2-0 -o $(CLI_DEMO_DIR)/kcol.json; \
$$PRED create SpinGlass --edges 0-1,1-2 -o $(CLI_DEMO_DIR)/sg.json; \
$$PRED create MaxCut --edges 0-1,1-2,2-0 -o $(CLI_DEMO_DIR)/maxcut.json; \
$$PRED create MVC --edges 0-1,1-2,2-3 -o $(CLI_DEMO_DIR)/mvc.json; \
$$PRED create MaximumMatching --edges 0-1,1-2,2-3 -o $(CLI_DEMO_DIR)/matching.json; \
$$PRED create Factoring --target 15 --bits-m 4 --bits-n 4 -o $(CLI_DEMO_DIR)/factoring.json; \
$$PRED create Factoring --target 21 --bits-m 3 --bits-n 3 -o $(CLI_DEMO_DIR)/factoring2.json; \
$$PRED create KColoring --k 3 --graph 0-1,1-2,2-0 -o $(CLI_DEMO_DIR)/kcol.json; \
$$PRED create SpinGlass --graph 0-1,1-2 -o $(CLI_DEMO_DIR)/sg.json; \
$$PRED create MaxCut --graph 0-1,1-2,2-0 -o $(CLI_DEMO_DIR)/maxcut.json; \
$$PRED create MVC --graph 0-1,1-2,2-3 -o $(CLI_DEMO_DIR)/mvc.json; \
$$PRED create MaximumMatching --graph 0-1,1-2,2-3 -o $(CLI_DEMO_DIR)/matching.json; \
$$PRED create Factoring --target 15 --m 4 --n 4 -o $(CLI_DEMO_DIR)/factoring.json; \
$$PRED create Factoring --target 21 --m 3 --n 3 -o $(CLI_DEMO_DIR)/factoring2.json; \
\
echo ""; \
echo "--- 9. evaluate: test configurations ---"; \
Expand Down Expand Up @@ -330,7 +330,7 @@ cli-demo: cli
echo ""; \
echo "--- 18. closed-loop: create → reduce → solve → verify ---"; \
echo "Creating a 6-vertex graph..."; \
$$PRED create MIS --edges 0-1,1-2,2-3,3-4,4-5,0-5,1-4 -o $(CLI_DEMO_DIR)/big.json; \
$$PRED create MIS --graph 0-1,1-2,2-3,3-4,4-5,0-5,1-4 -o $(CLI_DEMO_DIR)/big.json; \
echo "Solving with ILP..."; \
$$PRED solve $(CLI_DEMO_DIR)/big.json -o $(CLI_DEMO_DIR)/big_sol.json; \
echo "Reducing to QUBO and solving with brute-force..."; \
Expand Down
55 changes: 53 additions & 2 deletions problemreductions-cli/src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -203,11 +203,20 @@ TIP: Run `pred create <PROBLEM>` (no other flags) to see problem-specific help.
Flags by problem type:
MIS, MVC, MaxClique, MinDomSet --graph, --weights
MaxCut, MaxMatching, TSP --graph, --edge-weights
MaximalIS --graph, --weights
SAT, 3SAT/KSAT --num-vars, --clauses [--k]
QUBO --matrix
SpinGlass --graph, --couplings, --fields
KColoring --graph, --k
Factoring --target, --m, --n
BinPacking --sizes, --capacity
PaintShop --sequence
MaximumSetPacking --sets [--weights]
MinimumSetCovering --universe, --sets [--weights]
BicliqueCover --left, --right, --biedges, --k
BMF --matrix (0/1), --rank
CVP --basis, --target-vec [--bounds]
ILP, CircuitSAT (via reduction only)

Geometry graph variants (use slash notation, e.g., MIS/KingsSubgraph):
KingsSubgraph, TriangularSubgraph --positions (integer x,y pairs)
Expand Down Expand Up @@ -281,6 +290,42 @@ pub struct CreateArgs {
/// Radius for UnitDiskGraph [default: 1.0]
#[arg(long)]
pub radius: Option<f64>,
/// Item sizes for BinPacking (comma-separated, e.g., "3,3,2,2")
#[arg(long)]
pub sizes: Option<String>,
/// Bin capacity for BinPacking
#[arg(long)]
pub capacity: Option<String>,
/// Car paint sequence for PaintShop (comma-separated, each label appears exactly twice, e.g., "a,b,a,c,c,b")
#[arg(long)]
pub sequence: Option<String>,
/// Sets for SetPacking/SetCovering (semicolon-separated, e.g., "0,1;1,2;0,2")
#[arg(long)]
pub sets: Option<String>,
/// Universe size for MinimumSetCovering
#[arg(long)]
pub universe: Option<usize>,
/// Bipartite graph edges for BicliqueCover (e.g., "0-0,0-1,1-2" for left-right pairs)
#[arg(long)]
pub biedges: Option<String>,
/// Left partition size for BicliqueCover
#[arg(long)]
pub left: Option<usize>,
/// Right partition size for BicliqueCover
#[arg(long)]
pub right: Option<usize>,
/// Rank for BMF
#[arg(long)]
pub rank: Option<usize>,
/// Lattice basis for CVP (semicolon-separated column vectors, e.g., "1,0;0,1")
#[arg(long)]
pub basis: Option<String>,
/// Target vector for CVP (comma-separated, e.g., "0.5,0.5")
#[arg(long)]
pub target_vec: Option<String>,
/// Variable bounds for CVP as "lower,upper" (e.g., "-10,10") [default: -10,10]
#[arg(long, allow_hyphen_values = true)]
pub bounds: Option<String>,
}

#[derive(clap::Args)]
Expand Down Expand Up @@ -315,7 +360,7 @@ pub struct SolveArgs {
/// Solver: ilp (default) or brute-force
#[arg(long, default_value = "ilp")]
pub solver: String,
/// Timeout in seconds (0 = no limit) [default: 0]
/// Timeout in seconds (0 = no limit)
#[arg(long, default_value = "0")]
pub timeout: u64,
}
Expand Down Expand Up @@ -367,7 +412,13 @@ pub struct EvaluateArgs {
}

/// Print the after_help text for a subcommand on parse error.
///
/// Only matches the first line of the error message. Without this,
/// bare `pred` (no subcommand) would match "pred solve" in the
/// top-level workflow examples and incorrectly append the solve
/// subcommand's help text.
pub fn print_subcommand_help_hint(error_msg: &str) {
let first_line = error_msg.lines().next().unwrap_or("");
let subcmds = [
("pred solve", "solve"),
("pred reduce", "reduce"),
Expand All @@ -382,7 +433,7 @@ pub fn print_subcommand_help_hint(error_msg: &str) {
];
let cmd = Cli::command();
for (pattern, name) in subcmds {
if error_msg.contains(pattern) {
if first_line.contains(pattern) {
if let Some(sub) = cmd.find_subcommand(name) {
if let Some(help) = sub.get_after_help() {
eprintln!("\n{help}");
Expand Down
Loading