1+ // <copyright file="BPRunner.cs" company="Microsoft">
2+ // Copyright (c) Microsoft. All rights reserved.
3+ // </copyright>
4+
5+ namespace MetaOptimize . Cli
6+ {
7+ using System . Diagnostics ;
8+ using Gurobi ;
9+ using ZenLib ;
10+ using ZenLib . ModelChecking ;
11+
12+ /// <summary>
13+ /// Runner for Vector Bin Packing adversarial optimization.
14+ /// Finds item sizes that maximize the gap between optimal packing and First-Fit heuristics.
15+ /// </summary>
16+ /// <remarks>
17+ /// Flow: Configure bins → Create encoders → Run adversarial optimization → Display gap.
18+ ///
19+ /// Compares optimal bin packing solution against First-Fit variants (FF, FFDSum, FFDProd, FFDDiv).
20+ /// The adversarial generator finds item sizes where the heuristic uses significantly more bins
21+ /// than the optimal solution.
22+ ///
23+ /// Supports both Gurobi (MIP) and Zen (SMT) solvers via generic implementation.
24+ /// </remarks>
25+ public static class BPRunner
26+ {
27+ /// <summary>
28+ /// Runs Bin Packing adversarial optimization.
29+ /// Dispatches to the appropriate solver-specific implementation.
30+ /// </summary>
31+ /// <param name="opts">Command-line options containing bin packing parameters.</param>
32+ /// <exception cref="Exception">Thrown when an unsupported solver is specified.</exception>
33+ public static void Run ( CliOptions opts )
34+ {
35+ switch ( opts . SolverChoice )
36+ {
37+ case SolverChoice . OrTools :
38+ RunBinPacking ( new ORToolsSolver ( ) , opts ) ;
39+ break ;
40+ case SolverChoice . Zen :
41+ RunBinPacking ( new SolverZen ( ) , opts ) ;
42+ break ;
43+ case SolverChoice . Gurobi :
44+ RunBinPacking (
45+ new GurobiSOS ( timeout : opts . Timeout , verbose : Convert . ToInt32 ( opts . Verbose ) ) ,
46+ opts ) ;
47+ break ;
48+ default :
49+ throw new Exception ( $ "Unsupported solver: { opts . SolverChoice } . Valid options: OrTools, Gurobi, Zen") ;
50+ }
51+ }
52+
53+ /// <summary>
54+ /// Generic implementation of bin packing adversarial optimization.
55+ /// </summary>
56+ /// <typeparam name="TVar">Solver variable type (GRBVar or Zen).</typeparam>
57+ /// <typeparam name="TSolution">Solver solution type (GRBModel or ZenSolution).</typeparam>
58+ /// <param name="solver">The solver instance to use.</param>
59+ /// <param name="opts">Command-line options containing bin packing parameters.</param>
60+ /// <remarks>
61+ /// Creates three components:
62+ /// 1. VBPOptimalEncoder: Encodes the optimal bin packing problem
63+ /// 2. FFDItemCentricEncoder: Encodes the First-Fit heuristic behavior
64+ /// 3. VBPAdversarialInputGenerator: Finds item sizes maximizing the gap
65+ ///
66+ /// The optimization finds item sizes such that:
67+ /// - Optimal solution uses exactly opts.OptimalBins bins
68+ /// - FFD heuristic uses as many bins as possible
69+ /// - Gap = FFD bins - Optimal bins is maximized.
70+ /// </remarks>
71+ private static void RunBinPacking < TVar , TSolution > ( ISolver < TVar , TSolution > solver , CliOptions opts )
72+ {
73+ Console . WriteLine ( $ "Bins: { opts . NumBins } , Items: { opts . NumDemands } , Dimensions: { opts . NumDimensions } ") ;
74+ Console . WriteLine ( $ "Target optimal bins: { opts . OptimalBins } ") ;
75+ Console . WriteLine ( $ "FF Method: { opts . FFMethod } ") ;
76+
77+ // Parse bin capacities from comma-separated string
78+ var binCapacities = opts . BinCapacity . Split ( ',' ) . Select ( double . Parse ) . ToList ( ) ;
79+
80+ // Pad capacities to match number of dimensions
81+ while ( binCapacities . Count < opts . NumDimensions )
82+ {
83+ binCapacities . Add ( 1.00001 ) ;
84+ }
85+
86+ // Create bin configuration
87+ var bins = new Bins ( opts . NumBins , binCapacities ) ;
88+
89+ // Create optimal encoder - finds minimum bins needed
90+ var optimalEncoder = new VBPOptimalEncoder < TVar , TSolution > (
91+ solver , opts . NumDemands , opts . NumDimensions , BreakSymmetry : opts . BreakSymmetry ) ;
92+
93+ // Create FFD encoder - simulates First-Fit heuristic behavior
94+ var ffdEncoder = new FFDItemCentricEncoder < TVar , TSolution > (
95+ solver , opts . NumDemands , opts . NumDimensions ) ;
96+
97+ // Create adversarial generator - finds worst-case item sizes
98+ var adversarialGenerator = new VBPAdversarialInputGenerator < TVar , TSolution > (
99+ bins , opts . NumDemands , opts . NumDimensions ) ;
100+
101+ var timer = Stopwatch . StartNew ( ) ;
102+
103+ // Run bilevel optimization to find adversarial inputs
104+ List < IList < double > > demandList = null ;
105+ var ( optimalSolution , ffdSolution ) = adversarialGenerator . MaximizeOptimalityGapFFD (
106+ optimalEncoder , ffdEncoder ,
107+ opts . OptimalBins ,
108+ ffdMethod : opts . FFMethod ,
109+ itemList : demandList ,
110+ verbose : opts . Verbose ) ;
111+
112+ timer . Stop ( ) ;
113+
114+ // Display results
115+ Console . WriteLine ( "\n " + new string ( '=' , 60 ) ) ;
116+ Console . WriteLine ( "RESULTS:" ) ;
117+ Console . WriteLine ( $ "Optimal bins used: { optimalSolution . TotalNumBinsUsed } ") ;
118+ Console . WriteLine ( $ "{ opts . FFMethod } bins used: { ffdSolution . TotalNumBinsUsed } ") ;
119+ Console . WriteLine ( $ "Gap: { ffdSolution . TotalNumBinsUsed - optimalSolution . TotalNumBinsUsed } ") ;
120+ Console . WriteLine ( $ "Time: { timer . ElapsedMilliseconds } ms") ;
121+ Console . WriteLine ( new string ( '=' , 60 ) ) ;
122+
123+ // Verbose output: full solution details as JSON
124+ if ( opts . Verbose )
125+ {
126+ Console . WriteLine ( "\n Optimal Solution:" ) ;
127+ Console . WriteLine ( Newtonsoft . Json . JsonConvert . SerializeObject (
128+ optimalSolution , Newtonsoft . Json . Formatting . Indented ) ) ;
129+ Console . WriteLine ( "\n Heuristic Solution:" ) ;
130+ Console . WriteLine ( Newtonsoft . Json . JsonConvert . SerializeObject (
131+ ffdSolution , Newtonsoft . Json . Formatting . Indented ) ) ;
132+ }
133+ }
134+ }
135+ }
0 commit comments