Skip to content

Refactor: address KISS and DRY violations in variant-aware-paths branch #70

@GiggleLiu

Description

@GiggleLiu

Summary

Code review of the jg/variant-aware-paths branch identified several KISS (Keep It Simple) and DRY (Don't Repeat Yourself) violations worth addressing.


DRY Violations

1. Massive boilerplate duplication across vertex-weighted graph problems

Impact: High — ~325 duplicated lines across 5 files.

These files share nearly identical struct shapes, constructors, and accessors:

  • src/models/graph/maximum_independent_set.rs
  • src/models/graph/minimum_vertex_cover.rs
  • src/models/graph/maximum_clique.rs
  • src/models/graph/maximal_is.rs
  • src/models/graph/minimum_dominating_set.rs

Each repeats ~65 lines of identical code:

  • new(), with_weights(), from_graph(), from_graph_unit_weights() constructors
  • graph(), num_vertices(), num_edges(), edges(), has_edge(), weights_ref(), set_weights(), weights(), is_weighted() accessors
  • The same evaluate() pattern (check constraint → sum weights → return SolutionSize::Valid(total))

Suggestion: Extract a VertexWeightedProblem<G, W> wrapper struct or a macro to share the common boilerplate. Each problem would only need to define its constraint check and optimization direction.

2. PlanarGraph / BipartiteGraph manual VariantParam impls

File: src/graph_types.rs:14-46

These manually implement VariantParam + inventory::submit! (12 lines each) — exactly what impl_variant_param! does. The macro currently requires a cast: closure for the parent: variant, which these ZST marker types can't meaningfully provide.

Suggestion: Add a parent-only (no cast) arm to impl_variant_param!:

impl_variant_param!(PlanarGraph, "graph", parent: SimpleGraph);

3. source_graph() / target_graph() on ReductionEdge

File: src/rules/graph.rs:202-220

Two identical methods differing only in which field (source_variant / target_variant) they search.

Suggestion: Extract a single helper:

fn variant_graph(variant: &[(&str, &str)]) -> &str { ... }

4. graph_hierarchy() / weight_hierarchy()

File: src/rules/graph.rs:370-382

Same pattern with duplicate LazyLock<HashMap> statics, differing only in the category key.

Suggestion: Replace with fn hierarchy(&self, category: &str) -> &HashMap<...>.

5. is_graph_subtype() / is_weight_subtype() / is_k_subtype()

File: src/rules/graph.rs:355-367

Three trivial one-liner wrappers all calling is_subtype() with different category strings. Callers could use is_subtype("graph", ...) directly.


KISS Violations

1. find_shortest_path enumerates all paths

File: src/rules/graph.rs:542-553

Currently finds all simple paths (exponential) then picks the shortest. Dijkstra with MinimizeSteps cost already exists in the same file via find_cheapest_path.

2. Premature cost function zoo

File: src/rules/cost.rs

Defines 5 specialized cost structs plus CustomCost<F>. The closure-based CustomCost already handles any case. Unless the specific strategies are used in multiple call sites, most are speculative generality.

3. Double normalization of empty graph variant

variant_to_map() (src/rules/graph.rs:636-648) normalizes empty "graph" to "SimpleGraph". But source_graph() / target_graph() (lines 202-220) also do the same check. Same concern handled in two places.

4. to_json() is a 190-line monolith

File: src/rules/graph.rs:775-964

Single method doing 5 logically distinct things: collect nodes, collect edges, generate natural edges, sort, resolve indices. Could be split into helper methods.


Suggested Priority

  1. DRY Roadmap #1 (vertex-weighted problem boilerplate) — highest impact, most error-prone
  2. DRY Implement remaining reduction rules #2 (macro arm for parent-only) — small effort, improves consistency
  3. KISS Roadmap #1 (shortest path via Dijkstra) — correctness/perf improvement
  4. KISS feat: Feature parity with ProblemReductions.jl #4 (split to_json()) — readability
  5. Remaining items — nice-to-have cleanup

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions