Sign in

    mooncutstock

    Bounded heterogeneous cutting-stock planning with inventory, kerf, auditable cut maps and explicit optimality status

    cutting-stock
    kerf
    inventory
    optimization
    Download zip
    Author
    Version
    0.1.0
    License
    MIT
    Last updated
    21 hours ago
    Downloads
    3

    #MoonCutStock

    A pure MoonBit heterogeneous one-dimensional cutting-stock procurement planner. Combine material-matched demands with finite or unlimited stock of different prices, lengths and end trims; obtain auditable cutting instructions, inventory usage and reusable remnants. No external runtime solver or third-party module is required.

    Module: 3smallpig/mooncutstock@0.1.0 · MIT · participant/account: 3smallpig.

    #Install and integrate

    moon new shop cd shop moon add 3smallpig/mooncutstock@0.1.0

    Import "3smallpig/mooncutstock" @cutstock in your package's moon.pkg:

    let p = @cutstock.problem(
    [@cutstock.demand("beam", "wood", 40, 4)],
    [@cutstock.stock("short", "wood", 85, 7, 1),
    @cutstock.stock("long", "wood", 130, 12, -1)],
    kerf=2, min_remnant=5,
    ).unwrap()
    let s = @cutstock.solve(p).unwrap()
    println(@cutstock.text_report(p, s).unwrap())

    This instance yields purchase cost 19, 2 bars and exact fulfillment of 4 pieces. Solution::plan() is optional: inspect status before unwrapping it. Public constructors/decoders return Result; invalid geometry is not silently repaired. Use pkg.generated.mbti for the complete public interface.

    #Workflows

    moon run examples/procurement moon run examples/rollover moon run examples/scenarios moon run cmd/mooncutstock -- demo moon run cmd/mooncutstock -- help

    • Fabricators: optimize differently priced, trimmed bars; export count patterns and physical cut intervals. The procurement example costs 22 for 5 pieces.
    • Cable/fabric inventory tools: consume finite rolls, recover qualifying remnants, and construct a validated next-batch problem. The rollover example purchases nothing in its second batch; handling costs are not included.
    • Planning services: compare kerf, price and inventory scenarios using immutable problem snapshots and explicit completion status. The scenario example compares costs 20 and 40 when kerf changes from 0 to 2 length units.

    CLI commands take one inline document argument, not a filename or stdin. PowerShell example (single quotes protect the JSON document):

    $inputJson = '{"schema":"mooncutstock.problem/1","demands":[{"id":"a","material":"wood","length":40,"quantity":2}],"stocks":[{"id":"s","material":"wood","length":100,"cost":8,"quantity":-1,"trim_start":0,"trim_end":0}],"kerf":2,"min_remnant":5}' moon run cmd/mooncutstock -- solve-json $inputJson --format=text --patterns=50000 --nodes=200000

    solve-json, solve-csv, normalize-json, normalize-csv, audit-json are available through the host-independent run_command API as well. Invalid CLI input aborts with nonzero exit. CSV accepts --kerf=N --min-remnant=N; JSON requires these in its document. Both solvers accept --format=json|text, --patterns=N and --nodes=N. Duplicate/unknown options are rejected.

    #Exact meaning of the model

    • All lengths/costs are integers in caller-chosen consistent units; convert physical lengths and currency before calling. Materials compare as exact strings.
    • Each piece consumes length + kerf, including the final piece in a bar. Both end trims are removed first. This conservative rule is not a machine model that waives the final cut. There is no overproduction or mixed-material bar.
    • Minimize (purchase cost, purchased stock length, bar count) lexicographically. Remnant resale value is not part of this objective. Equal-objective layouts may differ. This is not merely fixed-bin feasibility or a 2D rectangle packer.
    • Stock quantity -1 means unlimited; 0 means unavailable. Remnants with length at least min_remnant become zero-purchase-cost stock; trims do not carry over.
    • audit rebuilds count-vector geometry and verifies exact fulfillment, stock limits and conservation: purchased = useful + kerf + trim + reusable + scrap. cutting_map gives half-open piece/kerf/remainder coordinates. The map verifier compares regenerated maps, not a separate independent geometry implementation.

    StatusInterpretation
    OptimalComplete pattern enumeration and complete bounded search; optimum proved within this model
    FeasibleBudgetAudited feasible incumbent, but enumeration/search budget prevented proof
    InfeasibleComplete enumeration/search proved no plan within this model
    UnknownBudgetNo incumbent and no proof; not evidence of infeasibility

    Defaults: 50,000 enumeration visits, 200,000 search work units. Maximum each is 1,000,000. Candidate checks and state visits both count as search work. Pattern budget limits recursive visits, not just output patterns. Memo table: 50,000 states. A work counter is not a time/memory guarantee; do not grant maximum limits on an untrusted request without host-level isolation. The greedy seed can miss a feasible solution; failure of that heuristic never proves infeasibility.

    #Interchange and bounds

    See format specification, architecture and security. Bounds: 12 demand types, 16 stock types, 128 total pieces, stock inventory -1 or 0..128, length 1..1,000,000 and nonnegative cost up to 1,000,000; kerf 0..1,000,000 and minimum remnant 1..1,000,000. Labels contain 1..128 UTF-16 code units and no ASCII control characters. End trims cannot exceed stock length. Rollover fails if retained types plus remnants exceed these bounds.

    Not provided: industrial-scale column generation, LP/MIP solver integration, 2D/3D nesting, scheduling, grain direction, tolerances, machine control, persistence, HTTP service or GUI. This release is not a safety-certified manufacturing system. Verify units, machine constraints and cut instructions before physical use.

    #Build and verify

    moon info && moon fmt moon fmt --check moon check --deny-warn moon test --deny-warn moon build --deny-warn python scripts/audit.py bash scripts/verify.sh wasm-gc

    CI checks wasm-gc, wasm, js, native on Linux with a digest-pinned official MoonBit snapshot. Initial development used the 20260920 development toolchain; release formatting and validation use moon 0.1.20261012 / moonc v0.10.15+943464bfa. Cross-target CI is the authoritative clean-runner evidence. 28 test blocks include 160 deterministic independent exhaustive-oracle cases, comparing the full objective and infeasibility; they are not 160 extra test blocks. There is no benchmark or formal verification claim.

    #Provenance and competition

    Original implementation assisted by an AI coding agent; no neighboring project source was copied. It implements standard enumeration, greedy packing, branch-and-bound and memoization ideas, not a newly invented optimization method. See references, AI disclosure, bounded semantic duplication review and changelog. Competition application and personal contact material are deliberately not part of this repository or package.

    BarMap

    pub(all) struct BarMap {
    bar : Int
    stock_id : String
    usable_start : Int
    usable_end : Int
    cuts : Array[Cut]
    remnant_start : Int
    remnant_end : Int
    reusable : Bool
    } derive(
    Debug
    )

    BarMap::to_repr

    Bound

    pub(all) struct Bound {
    cost : Int
    bars : Int
    possible : Bool
    } derive(Eq,
    Debug
    )

    Bound::equal

    fn Bound::equal(Bound, Bound) -> Bool

    Bound::not_equal

    fn Bound::not_equal(x : Bound, y : Bound) -> Bool

    Bound::to_repr

    Comparison

    pub(all) struct Comparison {
    cost_delta : Int
    purchased_delta : Int
    bars_delta : Int
    ordering : Int
    } derive(Eq,
    Debug
    )

    Comparison::equal

    fn Comparison::equal(Comparison, Comparison) -> Bool

    Comparison::not_equal

    fn Comparison::not_equal(x : Comparison, y : Comparison) -> Bool

    Cut

    pub(all) struct Cut {
    demand_id : String
    ordinal : Int
    start : Int
    end : Int
    kerf_end : Int
    } derive(Eq,
    Debug
    )

    Cut::equal

    fn Cut::equal(Cut, Cut) -> Bool

    Cut::not_equal

    fn Cut::not_equal(x : Cut, y : Cut) -> Bool

    Cut::to_repr

    Demand

    pub(all) struct Demand {
    id : String
    material : String
    length : Int
    quantity : Int
    } derive(Eq,
    Debug
    )

    Integer units are caller-defined and must be consistent throughout a problem.

    Demand::equal

    fn Demand::equal(Demand, Demand) -> Bool

    Demand::not_equal

    fn Demand::not_equal(x : Demand, y : Demand) -> Bool

    Demand::to_repr

    KerfScenario

    pub(all) struct KerfScenario {
    kerf : Int
    status : Status
    cost : Int?
    purchased : Int?
    search_work : Int
    } derive(
    Debug
    )

    Limits

    pub(all) struct Limits {
    patterns : Int
    nodes : Int
    } derive(Eq,
    Debug
    )

    Limits::equal

    fn Limits::equal(Limits, Limits) -> Bool

    Limits::not_equal

    fn Limits::not_equal(x : Limits, y : Limits) -> Bool

    Limits::to_repr

    Pattern

    pub struct Pattern {
    stock : Int
    counts : Array[Int]
    used : Int
    remaining : Int
    } derive(
    Debug
    )

    Pattern::counts

    fn Pattern::counts(self : Pattern) -> Array[Int]

    Pattern::pieces

    fn Pattern::pieces(self : Pattern) -> Int

    Pattern::remaining

    fn Pattern::remaining(self : Pattern) -> Int

    Pattern::stock_index

    fn Pattern::stock_index(self : Pattern) -> Int

    Pattern::to_repr

    Pattern::used

    fn Pattern::used(self : Pattern) -> Int

    PatternGroup

    pub(all) struct PatternGroup {
    stock_id : String
    counts : Array[Int]
    repetitions : Int
    remaining_each : Int
    } derive(
    Debug
    )

    PatternSet

    pub struct PatternSet {
    entries : Array[Pattern]
    complete : Bool
    visited : Int
    } derive(
    Debug
    )

    PatternSet::complete

    fn PatternSet::complete(self : PatternSet) -> Bool

    PatternSet::patterns

    fn PatternSet::patterns(self : PatternSet) -> Array[Pattern]

    PatternSet::visited

    fn PatternSet::visited(self : PatternSet) -> Int

    Plan

    pub struct Plan {
    bars : Array[Pattern]
    } derive(
    Debug
    )

    Plan::bar_count

    fn Plan::bar_count(self : Plan) -> Int

    Plan::bars

    fn Plan::bars(self : Plan) -> Array[Pattern]

    Plan::to_repr

    Problem

    pub struct Problem {
    demands : Array[Demand]
    stocks : Array[Stock]
    kerf : Int
    min_remnant : Int
    } derive(
    Debug
    )

    A validated owned snapshot. No mutable array is exposed to consumers.

    Problem::demands

    fn Problem::demands(self : Problem) -> Array[Demand]

    Problem::kerf

    fn Problem::kerf(self : Problem) -> Int

    Problem::min_remnant

    fn Problem::min_remnant(self : Problem) -> Int

    Problem::stocks

    fn Problem::stocks(self : Problem) -> Array[Stock]

    Problem::to_repr

    Remnant

    pub(all) struct Remnant {
    source_bar : Int
    source_stock : String
    material : String
    length : Int
    start : Int
    end : Int
    } derive(Eq,
    Debug
    )

    Remnant::equal

    fn Remnant::equal(Remnant, Remnant) -> Bool

    Remnant::not_equal

    fn Remnant::not_equal(x : Remnant, y : Remnant) -> Bool

    Remnant::to_repr

    Solution

    pub struct Solution {
    source : Problem
    status : Status
    plan : Plan?
    pattern_complete : Bool
    search_complete : Bool
    pattern_visits : Int
    pattern_count : Int
    work : Int
    pruned : Int
    } derive(
    Debug
    )

    Solution::dominated_states

    fn Solution::dominated_states(self : Solution) -> Int

    Solution::pattern_complete

    fn Solution::pattern_complete(self : Solution) -> Bool

    Solution::pattern_count

    fn Solution::pattern_count(self : Solution) -> Int

    Solution::pattern_visits

    fn Solution::pattern_visits(self : Solution) -> Int

    Solution::plan

    fn Solution::plan(self : Solution) -> Plan?

    Solution::search_complete

    fn Solution::search_complete(self : Solution) -> Bool

    Solution::status

    fn Solution::status(self : Solution) -> Status

    Solution::to_repr

    Solution::work

    fn Solution::work(self : Solution) -> Int

    Status

    pub(all) enum Status {
    Optimal
    FeasibleBudget
    Infeasible
    UnknownBudget
    } derive(Eq,
    Debug
    )

    Status::equal

    fn Status::equal(Status, Status) -> Bool

    Status::not_equal

    fn Status::not_equal(x : Status, y : Status) -> Bool

    Status::to_repr

    Stock

    pub(all) struct Stock {
    id : String
    material : String
    length : Int
    cost : Int
    quantity : Int
    trim_start : Int
    trim_end : Int
    } derive(Eq,
    Debug
    )

    quantity=-1 means unlimited; all other inventory quantities are nonnegative.

    Stock::equal

    fn Stock::equal(Stock, Stock) -> Bool

    Stock::not_equal

    fn Stock::not_equal(x : Stock, y : Stock) -> Bool

    Stock::to_repr

    Totals

    pub(all) struct Totals {
    cost : Int
    purchased : Int
    useful : Int
    kerf_loss : Int
    trim_loss : Int
    reusable : Int
    scrap : Int
    bars : Int
    pieces : Int
    } derive(Eq,
    Debug
    )

    Totals::equal

    fn Totals::equal(Totals, Totals) -> Bool

    Totals::not_equal

    fn Totals::not_equal(x : Totals, y : Totals) -> Bool

    Totals::to_repr

    audit

    fn audit(p : Problem, plan : Plan) -> Result[Totals, String]

    Rebuild every pattern from count vectors, independently of cached geometry.

    compare_plans

    fn compare_plans(p : Problem, baseline : Plan, candidate : Plan) -> Result[Comparison, String]

    Negative deltas mean the candidate improves on the baseline, for the same problem.

    cutting_map

    fn cutting_map(p : Problem, plan : Plan) -> Result[Array[BarMap], String]

    Half-open coordinates, in demand input order, with a kerf after every cut.

    decode_csv

    fn decode_csv(text : String, kerf? : Int, min_remnant? : Int) -> Result[Problem, String]

    decode_plan

    fn decode_plan(p : Problem, text : String) -> Result[Plan, String]

    decode_problem

    fn decode_problem(text : String) -> Result[Problem, String]

    default_limits

    fn default_limits() -> Limits

    demand

    fn demand(id : String, material : String, length : Int, quantity : Int) -> Demand

    demand_output

    fn demand_output(p : Problem, plan : Plan) -> Result[Array[Int], String]

    empty_plan

    fn empty_plan() -> Plan

    encode_csv

    fn encode_csv(p : Problem) -> String

    encode_plan

    fn encode_plan(p : Problem, plan : Plan) -> Result[String, String]

    External plan format stores only source decisions; cached geometry is never trusted.

    encode_problem

    fn encode_problem(p : Problem) -> String

    encode_solution

    fn encode_solution(p : Problem, s : Solution) -> Result[String, String]

    Bind optimality claims to the exact problem that was solved.

    enumerate_patterns

    fn enumerate_patterns(p : Problem, visit_limit? : Int) -> Result[PatternSet, String]

    Enumerates all nonempty feasible demand-count patterns, unless the visit budget ends.

    expand_groups

    fn expand_groups(p : Problem, groups : Array[PatternGroup]) -> Result[Plan, String]

    Read a compact pattern bill; the independent audit still requires exact fulfillment.

    greedy

    fn greedy(p : Problem) -> Plan?

    A deterministic feasible incumbent, not an optimality claim. None does not prove infeasibility.

    group_patterns

    fn group_patterns(p : Problem, plan : Plan) -> Result[Array[PatternGroup], String]

    A compact shop-floor bill of cutting patterns, in first-use order.

    help_text

    fn help_text() -> String

    kerf_scenarios

    fn kerf_scenarios(p : Problem, kerfs : Array[Int], limits? : Limits) -> Result[Array[KerfScenario], String]

    Each scenario uses an independent bounded solve; at most 16 scenarios per call.

    lower_bound

    fn lower_bound(p : Problem) -> Bound

    Necessary volume/rational-price bounds, not a promise that a feasible plan exists.

    pattern

    fn pattern(p : Problem, stock_index : Int, counts : Array[Int]) -> Result[Pattern, String]

    Each output piece consumes its length plus one kerf, including the final piece.

    plan_from_patterns

    fn plan_from_patterns(p : Problem, bars : Array[Pattern]) -> Result[Plan, String]

    Import a complete plan; rejects overproduction, shortages and inventory violations.

    problem

    fn problem(demands : Array[Demand], stocks : Array[Stock], kerf? : Int, min_remnant? : Int) -> Result[Problem, String]

    remnant_stocks

    fn remnant_stocks(p : Problem, plan : Plan) -> Result[Array[Stock], String]

    Aggregate equivalent remnants as zero-purchase-cost inventory with no inherited trims. Caller chooses whether and how handling/requalification costs should be added.

    remnants

    fn remnants(p : Problem, plan : Plan) -> Result[Array[Remnant], String]

    rollover

    fn rollover(p : Problem, plan : Plan, next_demands : Array[Demand], keep_inventory? : Bool) -> Result[Problem, String]

    Inventory transition is local data only; it never modifies the input problem. Fails rather than silently dropping stock types if the next problem exceeds bounds.

    run_command

    fn run_command(command : String, input : String, args? : Array[String]) -> Result[String, String]

    Pure command boundary for embedding in native, web, CI or editor hosts.

    solve

    fn solve(p : Problem, limits? : Limits) -> Result[Solution, String]

    Minimize (purchase cost, purchased length, bar count) lexicographically. A budget-limited result is never labeled Optimal or Infeasible.

    status_name

    fn status_name(status : Status) -> String

    stock

    fn stock(id : String, material : String, length : Int, cost : Int, quantity : Int, trim_start? : Int, trim_end? : Int) -> Stock

    stock_usage

    fn stock_usage(p : Problem, plan : Plan) -> Result[Array[Int], String]

    text_report

    fn text_report(p : Problem, s : Solution) -> Result[String, String]

    validate_limits

    fn validate_limits(limits : Limits) -> Result[Unit, String]

    verify_cutting_map

    fn verify_cutting_map(p : Problem, plan : Plan, maps : Array[BarMap]) -> Bool

    Verify exported coordinates without trusting the planner's interval arithmetic.

    with_costs

    fn with_costs(p : Problem, costs : Array[Int]) -> Result[Problem, String]

    with_inventory

    fn with_inventory(p : Problem, quantities : Array[Int]) -> Result[Problem, String]

    with_kerf

    fn with_kerf(p : Problem, kerf : Int) -> Result[Problem, String]