Skip to content

Commit 6328cd4

Browse files
authored
Merge pull request #6 from microsoft/metaopt-main
2 parents 18bfc9d + c01bc13 commit 6328cd4

23 files changed

+4007
-264
lines changed

.stylecop/StyleCop.props

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
<AdditionalFiles Include="..\.stylecop\stylecop.json">
55
<Visible>False</Visible>
66
</AdditionalFiles>
7-
<PackageReference Include="StyleCop.Analyzers" Version="1.1.118">
7+
<PackageReference Include="StyleCop.Analyzers">
88
<PrivateAssets>all</PrivateAssets>
99
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
1010
</PackageReference>

.vscode/launch.json

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
{
2+
"version": "0.2.0",
3+
"configurations": [
4+
{
5+
// Use IntelliSense to find out which attributes exist for C# debugging
6+
// Use hover for the description of the existing attributes
7+
// For further information visit https://github.com/dotnet/vscode-csharp/blob/main/debugger-launchjson.md
8+
"name": ".NET Core Launch (console)",
9+
"type": "coreclr",
10+
"request": "launch",
11+
"preLaunchTask": "build",
12+
// If you have changed target frameworks, make sure to update the program path.
13+
// "program": "${workspaceFolder}/MetaOptimize.Test/bin/Debug/net8.0/MetaOptimize.Test.dll",
14+
"program": "${workspaceFolder}/MetaOptimize.Cli/bin/Debug/net8.0/MetaOptimize.Cli.exe",
15+
"args": [],
16+
"cwd": "${workspaceFolder}/MetaOptimize.Cli",
17+
// For more information about the 'console' field, see https://aka.ms/VSCode-CS-LaunchJson-Console
18+
"console": "internalConsole",
19+
"stopAtEntry": false
20+
},
21+
{
22+
"name": ".NET Core Attach",
23+
"type": "coreclr",
24+
"request": "attach"
25+
}
26+
]
27+
}

.vscode/settings.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"dotnet.defaultSolution": "MetaOptimize.sln",
3+
"debugpy.debugJustMyCode": false,
4+
"jupyter.debugJustMyCode": false,
5+
"csharp.debug.justMyCode": false
6+
}

.vscode/tasks.json

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
{
2+
"version": "2.0.0",
3+
"tasks": [
4+
{
5+
"label": "build",
6+
"command": "dotnet",
7+
"type": "process",
8+
"args": [
9+
"build",
10+
"${workspaceFolder}/MetaOptimize.sln",
11+
"/property:GenerateFullPaths=true",
12+
"/consoleloggerparameters:NoSummary"
13+
],
14+
"problemMatcher": "$msCompile"
15+
},
16+
{
17+
"label": "publish",
18+
"command": "dotnet",
19+
"type": "process",
20+
"args": [
21+
"publish",
22+
"${workspaceFolder}/MetaOptimize.sln",
23+
"/property:GenerateFullPaths=true",
24+
"/consoleloggerparameters:NoSummary"
25+
],
26+
"problemMatcher": "$msCompile"
27+
},
28+
{
29+
"label": "watch",
30+
"command": "dotnet",
31+
"type": "process",
32+
"args": [
33+
"watch",
34+
"run",
35+
"--project",
36+
"${workspaceFolder}/MetaOptimize.sln"
37+
],
38+
"problemMatcher": "$msCompile"
39+
}
40+
]
41+
}

Directory.Build.props

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
<Project>
2+
<PropertyGroup>
3+
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
4+
</PropertyGroup>
5+
</Project>

Directory.Packages.props

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
<Project>
2+
<PropertyGroup>
3+
<NoWarn />
4+
<Features>strict</Features>
5+
<Nullable>disable</Nullable>
6+
<WarningLevel>9999</WarningLevel>
7+
<LangVersion>preview</LangVersion>
8+
<ImplicitUsings>enable</ImplicitUsings>
9+
<TargetFramework>net8.0</TargetFramework>
10+
<EnforceCodeStyleInBuild>True</EnforceCodeStyleInBuild>
11+
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
12+
13+
<!-- We must use an explicit platform due to the way Gurobi is packaged, so auto-detect it -->
14+
<PlatformTarget>x64</PlatformTarget>
15+
16+
<Company>Microsoft</Company>
17+
<Copyright>© Microsoft Corporation. All rights reserved.</Copyright>
18+
</PropertyGroup>
19+
20+
<!-- Central Package Version Management -->
21+
<ItemGroup>
22+
<PackageVersion Include="QuikGraph" Version="2.3.0" />
23+
<PackageVersion Include="ZenLib" Version="3.1.6" />
24+
<PackageVersion Include="Google.OrTools" Version="9.12.4544" />
25+
<PackageVersion Include="CommandLineParser" Version="2.8.0" />
26+
<PackageVersion Include="Gurobi.Optimizer" Version="11.0.3" />
27+
<PackageVersion Include="Microsoft.CSharp" Version="4.7.0" />
28+
<PackageVersion Include="Newtonsoft.Json" Version="13.0.3" />
29+
<PackageVersion Include="NLog" Version="5.2.8" />
30+
<PackageVersion Include="StyleCop" Version="6.2.0" />
31+
<PackageVersion Include="StyleCop.MSBuild" Version="6.2.0" />
32+
<PackageVersion Include="StyleCop.Analyzers" Version="1.1.118" />
33+
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.6.3" />
34+
<PackageVersion Include="MSTest.TestAdapter" Version="3.1.1" />
35+
<PackageVersion Include="MSTest.TestFramework" Version="3.1.1" />
36+
<PackageVersion Include="coverlet.collector" Version="6.0.0" />
37+
<PackageVersion Include="MathNet.Numerics" Version="5.0.0" />
38+
<PackageVersion Include="Meta.Numerics" Version="4.1.4" />
39+
<PackageVersion Include="CsvHelper" Version="30.0.1" />
40+
<PackageVersion Include="System.Numerics.Tensors" Version="0.1.0" />
41+
</ItemGroup>
42+
</Project>

MetaOptimize.Cli/BPRunner.cs

Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
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("\nOptimal Solution:");
127+
Console.WriteLine(Newtonsoft.Json.JsonConvert.SerializeObject(
128+
optimalSolution, Newtonsoft.Json.Formatting.Indented));
129+
Console.WriteLine("\nHeuristic Solution:");
130+
Console.WriteLine(Newtonsoft.Json.JsonConvert.SerializeObject(
131+
ffdSolution, Newtonsoft.Json.Formatting.Indented));
132+
}
133+
}
134+
}
135+
}

0 commit comments

Comments
 (0)