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
2 changes: 1 addition & 1 deletion .stylecop/StyleCop.props
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<AdditionalFiles Include="..\.stylecop\stylecop.json">
<Visible>False</Visible>
</AdditionalFiles>
<PackageReference Include="StyleCop.Analyzers" Version="1.1.118">
<PackageReference Include="StyleCop.Analyzers">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
Expand Down
27 changes: 27 additions & 0 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
{
"version": "0.2.0",
"configurations": [
{
// Use IntelliSense to find out which attributes exist for C# debugging
// Use hover for the description of the existing attributes
// For further information visit https://github.com/dotnet/vscode-csharp/blob/main/debugger-launchjson.md
"name": ".NET Core Launch (console)",
"type": "coreclr",
"request": "launch",
"preLaunchTask": "build",
// If you have changed target frameworks, make sure to update the program path.
// "program": "${workspaceFolder}/MetaOptimize.Test/bin/Debug/net8.0/MetaOptimize.Test.dll",
"program": "${workspaceFolder}/MetaOptimize.Cli/bin/Debug/net8.0/MetaOptimize.Cli.exe",
"args": [],
"cwd": "${workspaceFolder}/MetaOptimize.Cli",
// For more information about the 'console' field, see https://aka.ms/VSCode-CS-LaunchJson-Console
"console": "internalConsole",
"stopAtEntry": false
},
{
"name": ".NET Core Attach",
"type": "coreclr",
"request": "attach"
}
]
}
6 changes: 6 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"dotnet.defaultSolution": "MetaOptimize.sln",
"debugpy.debugJustMyCode": false,
"jupyter.debugJustMyCode": false,
"csharp.debug.justMyCode": false
}
41 changes: 41 additions & 0 deletions .vscode/tasks.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
{
"version": "2.0.0",
"tasks": [
{
"label": "build",
"command": "dotnet",
"type": "process",
"args": [
"build",
"${workspaceFolder}/MetaOptimize.sln",
"/property:GenerateFullPaths=true",
"/consoleloggerparameters:NoSummary"
],
"problemMatcher": "$msCompile"
},
{
"label": "publish",
"command": "dotnet",
"type": "process",
"args": [
"publish",
"${workspaceFolder}/MetaOptimize.sln",
"/property:GenerateFullPaths=true",
"/consoleloggerparameters:NoSummary"
],
"problemMatcher": "$msCompile"
},
{
"label": "watch",
"command": "dotnet",
"type": "process",
"args": [
"watch",
"run",
"--project",
"${workspaceFolder}/MetaOptimize.sln"
],
"problemMatcher": "$msCompile"
}
]
}
5 changes: 5 additions & 0 deletions Directory.Build.props
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<Project>
<PropertyGroup>
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
</PropertyGroup>
</Project>
42 changes: 42 additions & 0 deletions Directory.Packages.props
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
<Project>
<PropertyGroup>
<NoWarn />
<Features>strict</Features>
<Nullable>disable</Nullable>
<WarningLevel>9999</WarningLevel>
<LangVersion>preview</LangVersion>
<ImplicitUsings>enable</ImplicitUsings>
<TargetFramework>net8.0</TargetFramework>
<EnforceCodeStyleInBuild>True</EnforceCodeStyleInBuild>
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>

<!-- We must use an explicit platform due to the way Gurobi is packaged, so auto-detect it -->
<PlatformTarget>x64</PlatformTarget>

<Company>Microsoft</Company>
<Copyright>© Microsoft Corporation. All rights reserved.</Copyright>
</PropertyGroup>

<!-- Central Package Version Management -->
<ItemGroup>
<PackageVersion Include="QuikGraph" Version="2.3.0" />
<PackageVersion Include="ZenLib" Version="3.1.6" />
<PackageVersion Include="Google.OrTools" Version="9.12.4544" />
<PackageVersion Include="CommandLineParser" Version="2.8.0" />
<PackageVersion Include="Gurobi.Optimizer" Version="11.0.3" />
<PackageVersion Include="Microsoft.CSharp" Version="4.7.0" />
<PackageVersion Include="Newtonsoft.Json" Version="13.0.3" />
<PackageVersion Include="NLog" Version="5.2.8" />
<PackageVersion Include="StyleCop" Version="6.2.0" />
<PackageVersion Include="StyleCop.MSBuild" Version="6.2.0" />
<PackageVersion Include="StyleCop.Analyzers" Version="1.1.118" />
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.6.3" />
<PackageVersion Include="MSTest.TestAdapter" Version="3.1.1" />
<PackageVersion Include="MSTest.TestFramework" Version="3.1.1" />
<PackageVersion Include="coverlet.collector" Version="6.0.0" />
<PackageVersion Include="MathNet.Numerics" Version="5.0.0" />
<PackageVersion Include="Meta.Numerics" Version="4.1.4" />
<PackageVersion Include="CsvHelper" Version="30.0.1" />
<PackageVersion Include="System.Numerics.Tensors" Version="0.1.0" />
</ItemGroup>
</Project>
135 changes: 135 additions & 0 deletions MetaOptimize.Cli/BPRunner.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
// <copyright file="BPRunner.cs" company="Microsoft">
// Copyright (c) Microsoft. All rights reserved.
// </copyright>

namespace MetaOptimize.Cli
{
using System.Diagnostics;
using Gurobi;
using ZenLib;
using ZenLib.ModelChecking;

/// <summary>
/// Runner for Vector Bin Packing adversarial optimization.
/// Finds item sizes that maximize the gap between optimal packing and First-Fit heuristics.
/// </summary>
/// <remarks>
/// Flow: Configure bins → Create encoders → Run adversarial optimization → Display gap.
///
/// Compares optimal bin packing solution against First-Fit variants (FF, FFDSum, FFDProd, FFDDiv).
/// The adversarial generator finds item sizes where the heuristic uses significantly more bins
/// than the optimal solution.
///
/// Supports both Gurobi (MIP) and Zen (SMT) solvers via generic implementation.
/// </remarks>
public static class BPRunner
{
/// <summary>
/// Runs Bin Packing adversarial optimization.
/// Dispatches to the appropriate solver-specific implementation.
/// </summary>
/// <param name="opts">Command-line options containing bin packing parameters.</param>
/// <exception cref="Exception">Thrown when an unsupported solver is specified.</exception>
public static void Run(CliOptions opts)
{
switch (opts.SolverChoice)
{
case SolverChoice.OrTools:
RunBinPacking(new ORToolsSolver(), opts);
break;
case SolverChoice.Zen:
RunBinPacking(new SolverZen(), opts);
break;
case SolverChoice.Gurobi:
RunBinPacking(
new GurobiSOS(timeout: opts.Timeout, verbose: Convert.ToInt32(opts.Verbose)),
opts);
break;
default:
throw new Exception($"Unsupported solver: {opts.SolverChoice}. Valid options: OrTools, Gurobi, Zen");
}
}

/// <summary>
/// Generic implementation of bin packing adversarial optimization.
/// </summary>
/// <typeparam name="TVar">Solver variable type (GRBVar or Zen).</typeparam>
/// <typeparam name="TSolution">Solver solution type (GRBModel or ZenSolution).</typeparam>
/// <param name="solver">The solver instance to use.</param>
/// <param name="opts">Command-line options containing bin packing parameters.</param>
/// <remarks>
/// Creates three components:
/// 1. VBPOptimalEncoder: Encodes the optimal bin packing problem
/// 2. FFDItemCentricEncoder: Encodes the First-Fit heuristic behavior
/// 3. VBPAdversarialInputGenerator: Finds item sizes maximizing the gap
///
/// The optimization finds item sizes such that:
/// - Optimal solution uses exactly opts.OptimalBins bins
/// - FFD heuristic uses as many bins as possible
/// - Gap = FFD bins - Optimal bins is maximized.
/// </remarks>
private static void RunBinPacking<TVar, TSolution>(ISolver<TVar, TSolution> solver, CliOptions opts)
{
Console.WriteLine($"Bins: {opts.NumBins}, Items: {opts.NumDemands}, Dimensions: {opts.NumDimensions}");
Console.WriteLine($"Target optimal bins: {opts.OptimalBins}");
Console.WriteLine($"FF Method: {opts.FFMethod}");

// Parse bin capacities from comma-separated string
var binCapacities = opts.BinCapacity.Split(',').Select(double.Parse).ToList();

// Pad capacities to match number of dimensions
while (binCapacities.Count < opts.NumDimensions)
{
binCapacities.Add(1.00001);
}

// Create bin configuration
var bins = new Bins(opts.NumBins, binCapacities);

// Create optimal encoder - finds minimum bins needed
var optimalEncoder = new VBPOptimalEncoder<TVar, TSolution>(
solver, opts.NumDemands, opts.NumDimensions, BreakSymmetry: opts.BreakSymmetry);

// Create FFD encoder - simulates First-Fit heuristic behavior
var ffdEncoder = new FFDItemCentricEncoder<TVar, TSolution>(
solver, opts.NumDemands, opts.NumDimensions);

// Create adversarial generator - finds worst-case item sizes
var adversarialGenerator = new VBPAdversarialInputGenerator<TVar, TSolution>(
bins, opts.NumDemands, opts.NumDimensions);

var timer = Stopwatch.StartNew();

// Run bilevel optimization to find adversarial inputs
List<IList<double>> demandList = null;
var (optimalSolution, ffdSolution) = adversarialGenerator.MaximizeOptimalityGapFFD(
optimalEncoder, ffdEncoder,
opts.OptimalBins,
ffdMethod: opts.FFMethod,
itemList: demandList,
verbose: opts.Verbose);

timer.Stop();

// Display results
Console.WriteLine("\n" + new string('=', 60));
Console.WriteLine("RESULTS:");
Console.WriteLine($"Optimal bins used: {optimalSolution.TotalNumBinsUsed}");
Console.WriteLine($"{opts.FFMethod} bins used: {ffdSolution.TotalNumBinsUsed}");
Console.WriteLine($"Gap: {ffdSolution.TotalNumBinsUsed - optimalSolution.TotalNumBinsUsed}");
Console.WriteLine($"Time: {timer.ElapsedMilliseconds}ms");
Console.WriteLine(new string('=', 60));

// Verbose output: full solution details as JSON
if (opts.Verbose)
{
Console.WriteLine("\nOptimal Solution:");
Console.WriteLine(Newtonsoft.Json.JsonConvert.SerializeObject(
optimalSolution, Newtonsoft.Json.Formatting.Indented));
Console.WriteLine("\nHeuristic Solution:");
Console.WriteLine(Newtonsoft.Json.JsonConvert.SerializeObject(
ffdSolution, Newtonsoft.Json.Formatting.Indented));
}
}
}
}
Loading
Loading