diff --git a/MetaOptimize.Cli/Program.cs b/MetaOptimize.Cli/Program.cs new file mode 100644 index 00000000..dd82a5dd --- /dev/null +++ b/MetaOptimize.Cli/Program.cs @@ -0,0 +1,608 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// + +namespace MetaOptimize.Cli +{ + using System; + using System.Diagnostics; + using CommandLine; + using Gurobi; + using MetaOptimize; + using ZenLib; + + /// + /// Main entry point for the program. + /// + public class Program + { + /// + /// checks whether we get the solution we expect after running the solvers. + /// + /// + public static void TEExampleMain(string[] args) + { + var topology = new Topology(); + topology.AddNode("a"); + topology.AddNode("b"); + topology.AddNode("c"); + topology.AddNode("d"); + topology.AddEdge("a", "b", capacity: 10); + topology.AddEdge("a", "c", capacity: 10); + topology.AddEdge("b", "d", capacity: 10); + topology.AddEdge("c", "d", capacity: 10); + + var partition = topology.RandomPartition(2); + // create the optimal encoder. + var solverG = new GurobiSOS(); + var optimalEncoderG = new TEMaxFlowOptimalEncoder(solverG, maxNumPaths: 1); + var popEncoderG = new PopEncoder(solverG, maxNumPaths: 1, numPartitions: 2, demandPartitions: partition); + var adversarialInputGenerator = new TEAdversarialInputGenerator(topology, maxNumPaths: 1); + + var (optimalSolutionG, popSolutionG) = adversarialInputGenerator.MaximizeOptimalityGap(optimalEncoderG, popEncoderG); + Console.WriteLine("Optimal:"); + Console.WriteLine(Newtonsoft.Json.JsonConvert.SerializeObject(optimalSolutionG, Newtonsoft.Json.Formatting.Indented)); + Console.WriteLine("****"); + Console.WriteLine("Heuristic:"); + Console.WriteLine(Newtonsoft.Json.JsonConvert.SerializeObject(popSolutionG, Newtonsoft.Json.Formatting.Indented)); + Console.WriteLine("****"); + + var optimal = optimalSolutionG.MaxObjective; + var heuristic = popSolutionG.MaxObjective; + Console.WriteLine($"optimalG={optimal}, heuristicG={heuristic}"); + + var demands = new Dictionary<(string, string), double>(optimalSolutionG.Demands); + var optGSolver = new GurobiSOS(); + optimalEncoderG = new TEMaxFlowOptimalEncoder(optGSolver, maxNumPaths: 1); + var popGSolver = new GurobiSOS(); + popEncoderG = new PopEncoder(popGSolver, maxNumPaths: 1, numPartitions: 2, demandPartitions: partition); + Utils.checkSolution(topology, popEncoderG, optimalEncoderG, heuristic, optimal, demands, "gurobiCheck"); + } + + /// + /// Use this function to test our theorem for VBP. + /// (see theorem 1 in our NSDI24 Paper). + /// + public static void vbpMain(string[] args) + { + // OPT = 2m + 3n + // HUE = 4m + 6n + // num jobs = 6m + 9n + var solverG = new GurobiSOS(verbose: 0); + + for (int m = 1; m < 10; m++) + { + for (int n = 0; n < 2; n++) + { + Console.WriteLine(String.Format("============ m = {0}, n = {1}", m, n)); + var binSize = new List(); + binSize.Add(1.0001); + binSize.Add(1.0001); + var bins = new Bins(4 * m + 6 * n, binSize); + // TODO: need to change var name to be appropriate for the problem. + var demands = new Dictionary>(); + int nxt_key = 0; + + // TODO: need a comment that describes what the constants are here. you also may benefit from changing the constants to have a name. + for (int i = 0; i < m; i++) + { + demands[nxt_key] = new List() { 0.92, 0.0 }; + nxt_key += 1; + } + for (int i = 0; i < m; i++) + { + demands[nxt_key] = new List() { 0.91, 0.01 }; + nxt_key += 1; + } + for (int i = 0; i < n; i++) + { + demands[nxt_key] = new List() { 0.48, 0.2 }; + nxt_key += 1; + } + for (int i = 0; i < n; i++) + { + demands[nxt_key] = new List() { 0.68, 0 }; + nxt_key += 1; + } + for (int i = 0; i < n; i++) + { + demands[nxt_key] = new List() { 0.52, 0.12 }; + nxt_key += 1; + } + for (int i = 0; i < n; i++) + { + demands[nxt_key] = new List() { 0.32, 0.32 }; + nxt_key += 1; + } + for (int i = 0; i < n; i++) + { + demands[nxt_key] = new List() { 0.19, 0.45 }; + nxt_key += 1; + demands[nxt_key] = new List() { 0.42, 0.22 }; + nxt_key += 1; + } + for (int i = 0; i < n; i++) + { + demands[nxt_key] = new List() { 0.1, 0.54 }; + nxt_key += 1; + demands[nxt_key] = new List() { 0.1, 0.54 }; + nxt_key += 1; + } + for (int i = 0; i < n; i++) + { + demands[nxt_key] = new List() { 0.1, 0.53 }; + nxt_key += 1; + } + for (int i = 0; i < m; i++) + { + demands[nxt_key] = new List() { 0.06, 0.48 }; + nxt_key += 1; + } + for (int i = 0; i < m; i++) + { + demands[nxt_key] = new List() { 0.07, 0.47 }; + nxt_key += 1; + } + for (int i = 0; i < m; i++) + { + demands[nxt_key] = new List() { 0.01, 0.53 }; + nxt_key += 1; + } + for (int i = 0; i < m; i++) + { + demands[nxt_key] = new List() { 0.03, 0.51 }; + nxt_key += 1; + } + for (int i = 0; i < demands.Count - 1; i++) + { + double total_sum_1 = 0; + double total_sum_2 = 0; + for (int j = 0; j < demands[0].Count; j++) + { + total_sum_1 += demands[i][j]; + total_sum_2 += demands[i + 1][j]; + } + Debug.Assert(total_sum_1 >= total_sum_2 - 0.00001); + } + solverG.CleanAll(); + var optimalEncoder = new VBPOptimalEncoder(solverG, demands.Count, demands[0].Count); + var optimalEncoding = optimalEncoder.Encoding(bins, inputEqualityConstraints: demands, verbose: false); + var solverSolutionOptimal = optimalEncoder.Solver.Maximize(optimalEncoding.MaximizationObjective); + var optimizationSolutionOptimal = (VBPOptimizationSolution)optimalEncoder.GetSolution(solverSolutionOptimal); + Console.WriteLine( + String.Format("===== OPT {0}", optimizationSolutionOptimal.TotalNumBinsUsed)); + + solverG.CleanAll(); + var ffdEncoder = new FFDItemCentricEncoder(solverG, demands.Count, demands[0].Count); + var ffdEncoding = ffdEncoder.Encoding(bins, inputEqualityConstraints: demands, verbose: false); + var solverSolutionFFD = optimalEncoder.Solver.Maximize(ffdEncoding.MaximizationObjective); + var solutionFFD = (VBPOptimizationSolution)ffdEncoder.GetSolution(solverSolutionFFD); + Console.WriteLine( + String.Format("===== HUE {0}", solutionFFD.TotalNumBinsUsed)); + Debug.Assert(optimizationSolutionOptimal.TotalNumBinsUsed * 2 == solutionFFD.TotalNumBinsUsed); + } + } + } + + /// + /// test MetaOpt on VBP. + /// + /// TODO: specify how this function is different from the previous. + public static void Main(string[] args) + { + var binSize = new List(); + binSize.Add(1.00001); + binSize.Add(1.00001); + var bins = new Bins(6, binSize); + var numDemands = 9; + var numDimensions = 2; + var optimalBins = 3; + var ffdMethod = FFDMethodChoice.FFDSum; + List> demandList = null; + double perIterationTimeout = 1000; + // double perIterationTimeout = double.PositiveInfinity; + var solverG = new GurobiSOS(timeout: perIterationTimeout, verbose: 1); + var optimalEncoder = new VBPOptimalEncoder(solverG, numDemands, numDimensions, BreakSymmetry: false); + var ffdEncoder = new FFDItemCentricEncoder(solverG, numDemands, numDimensions); + var adversarialGenerator = new VBPAdversarialInputGenerator(bins, numDemands, numDimensions); + var (optimalSolutionG, ffdSolutionG) = adversarialGenerator.MaximizeOptimalityGapFFD(optimalEncoder, ffdEncoder, + optimalBins, ffdMethod: ffdMethod, itemList: demandList, verbose: true); + Console.WriteLine("Optimal:"); + Console.WriteLine(Newtonsoft.Json.JsonConvert.SerializeObject(optimalSolutionG, Newtonsoft.Json.Formatting.Indented)); + Console.WriteLine("****"); + Console.WriteLine("Heuristic:"); + Console.WriteLine(Newtonsoft.Json.JsonConvert.SerializeObject(ffdSolutionG, Newtonsoft.Json.Formatting.Indented)); + Console.WriteLine("****"); + Console.WriteLine("Optimal number of bins: " + optimalSolutionG.TotalNumBinsUsed); + Console.WriteLine("FFD number of bins: " + ffdSolutionG.TotalNumBinsUsed); + } + + /// + /// test case for SP-PIFO. + /// + public static void PIFOTestMain(string[] args) + { + int maxRank = 8; + int numPackets = 15; + int numQueues = 4; + int splitQueue = 2; + int splitRank = 5; + var solverG = new GurobiSOS(verbose: 0); + + var packetRankEqualityConstraint = new Dictionary(); + packetRankEqualityConstraint[0] = 7; + packetRankEqualityConstraint[1] = 2; + packetRankEqualityConstraint[2] = 1; + packetRankEqualityConstraint[3] = 0; + packetRankEqualityConstraint[4] = 7; + packetRankEqualityConstraint[5] = 7; + packetRankEqualityConstraint[6] = 2; + packetRankEqualityConstraint[7] = 1; + packetRankEqualityConstraint[8] = 0; + packetRankEqualityConstraint[9] = 2; + packetRankEqualityConstraint[10] = 1; + packetRankEqualityConstraint[11] = 0; + packetRankEqualityConstraint[12] = 2; + packetRankEqualityConstraint[13] = 1; + packetRankEqualityConstraint[14] = 0; + solverG.CleanAll(); + var optimalEncoder = new PIFOAvgDelayOptimalEncoder(solverG, numPackets, maxRank); + var optimalEncoding = optimalEncoder.Encoding(rankEqualityConstraints: packetRankEqualityConstraint); + var solverSolutionOptimal = optimalEncoder.Solver.Maximize(optimalEncoding.MaximizationObjective); + var optimizationSolutionOptimal = (PIFOOptimizationSolution)optimalEncoder.GetSolution(solverSolutionOptimal); + Console.WriteLine("Optimal:"); + Console.WriteLine(Newtonsoft.Json.JsonConvert.SerializeObject(optimizationSolutionOptimal, Newtonsoft.Json.Formatting.Indented)); + Console.WriteLine("****"); + Console.WriteLine("===== OPT {0}", optimizationSolutionOptimal.Cost); + + solverG.CleanAll(); + var heuristicEncoder = new ModifiedSPPIFOAvgDelayEncoder(solverG, numPackets, splitQueue, numQueues, splitRank, maxRank); + var heuristicEncoding = heuristicEncoder.Encoding(rankEqualityConstraints: packetRankEqualityConstraint); + var solverSolutionHeuristic = optimalEncoder.Solver.Maximize(heuristicEncoding.MaximizationObjective); + var solutionHeuristic = (PIFOOptimizationSolution)heuristicEncoder.GetSolution(solverSolutionHeuristic); + Console.WriteLine("Heuristic:"); + Console.WriteLine(Newtonsoft.Json.JsonConvert.SerializeObject(solutionHeuristic, Newtonsoft.Json.Formatting.Indented)); + Console.WriteLine("****"); + Console.WriteLine("===== HUE {0}", solutionHeuristic.Cost); + } + + /// + /// test MetaOpt on PIFO. + /// + public static void PIFOMain(string[] args) + { + int maxRank = 8; + int numPackets = 18; + int numQueues = 4; + // int splitQueue = 2; + // int splitRank = 4; + int maxQueueSize = 12; + int windowSize = 12; + double burstParam = 0.1; + + var solverG = new GurobiSOS(verbose: 1, timeout: 1000); + // var optimalEncoder = new PIFOAvgDelayOptimalEncoder(solverG, numPackets, maxRank); + // var heuristicEncoder = new SPPIFOAvgDelayEncoder(solverG, numPackets, numQueues, maxRank); + // var optimalEncoder = new SPPIFOAvgDelayEncoder(solverG, numPackets, numQueues, maxRank); + // var heuristicEncoder = new ModifiedSPPIFOAvgDelayEncoder(solverG, numPackets, splitQueue, numQueues, + // splitRank, maxRank); + // var optimalEncoder = new PIFOWithDropAvgDelayEncoder(solverG, numPackets, maxRank, maxQueueSize); + var H1 = new SPPIFOWithDropAvgDelayEncoder(solverG, numPackets, numQueues, maxRank, maxQueueSize); + var H2 = new AIFOAvgDelayEncoder(solverG, numPackets, maxRank, maxQueueSize, windowSize, burstParam); + + var adversarialGenerator = new PIFOAdversarialInputGenerator(numPackets, maxRank); + var (optimalSolutionG, heuristicSolutionG) = adversarialGenerator.MaximizeOptimalityGap(H1, + H2, verbose: true); + Console.WriteLine("Optimal:"); + Console.WriteLine(Newtonsoft.Json.JsonConvert.SerializeObject(optimalSolutionG, Newtonsoft.Json.Formatting.Indented)); + Console.WriteLine("****"); + Console.WriteLine("Heuristic:"); + Console.WriteLine(Newtonsoft.Json.JsonConvert.SerializeObject(heuristicSolutionG, Newtonsoft.Json.Formatting.Indented)); + Console.WriteLine("****"); + Console.WriteLine("Optimal cost: " + optimalSolutionG.Cost); + Console.WriteLine("Heuristic cost: " + heuristicSolutionG.Cost); + + var orderToRankOpt = new Dictionary(); + var orderToRankHeu = new Dictionary(); + for (int pid = 0; pid < numPackets; pid++) { + if (optimalSolutionG.Admit[pid] == 1) { + orderToRankOpt[optimalSolutionG.Order[pid]] = optimalSolutionG.Ranks[pid]; + } + if (heuristicSolutionG.Admit[pid] == 1) { + orderToRankHeu[heuristicSolutionG.Order[pid]] = heuristicSolutionG.Ranks[pid]; + } + } + + int numInvOpt = 0; + int numInvHeu = 0; + for (int pid = 0; pid < numPackets; pid++) + { + numInvOpt += ComputeInversionNum(optimalSolutionG, orderToRankOpt, pid); + numInvHeu += ComputeInversionNum(heuristicSolutionG, orderToRankHeu, pid); + } + Console.WriteLine("number of inversions in OPT: " + numInvOpt); + Console.WriteLine("number of inversions in HEU: " + numInvHeu); + } + + private static int ComputeInversionNum(PIFOOptimizationSolution optimalSolutionG, Dictionary orderToRankOpt, int pid) + { + int numInvOpt = 0; + if (optimalSolutionG.Admit[pid] >= 0.98) + { + int currOrder = optimalSolutionG.Order[pid]; + for (int prev = 0; prev < currOrder; prev++) + { + if (orderToRankOpt[prev] > optimalSolutionG.Ranks[pid]) + { + numInvOpt += 1; + } + } + } + else + { + foreach (var (order, rank) in orderToRankOpt) + { + if (rank > optimalSolutionG.Ranks[pid]) + { + numInvOpt += 1; + } + } + } + + return numInvOpt; + } + + /// + /// Experiments for NSDI. + /// + public static void NSDIMain(string[] args) + { + // NSDIExp.compareGapDelayDiffMethodsDP(); + // NSDIExp.compareLargeScaleGapDelayDiffMethodsDP(); + // NSDIExp.compareGapDelayDiffMethodsPop(); + // NSDIExp.AblationStudyClusteringOnDP(); + // NSDIExp.BlackBoxParameterTunning(); + NSDIExp.AddRealisticConstraintsDP(); + // NSDIExp.gapThresholdDemandPinningForDifferentTopologies(); + // NSDIExp.ImpactNumPathsPartitionsExpectedPop(); + // NSDIExp.AblationStudyClusteringOnDP(); + // NSDIExp.BlackBoxParameterTunning(); + // NSDIExp.AnalyzeModifiedDP(); + // NSDIExp.ImpactNumNodesRadixSmallWordTopoDemandPinning(); + // NSDIExp.ImpactNumSamplesExpectedPop(); + // NSDIExp.AnalyzeParallelHeuristics(); + } + + /// + /// Experiments for hotnets. + /// + public static void hotnetsMain(string[] args) + { + // var topology = Topology.RandomRegularGraph(8, 7, 1, seed: 0); + // var topology = Topology.SmallWordGraph(5, 4, 1); + // foreach (var edge in topology.GetAllEdges()) { + // Console.WriteLine(edge.Source + "_" + edge.Target); + // } + // foreach (var pair in topology.GetNodePairs()) { + // if (!topology.ContaintsEdge(pair.Item1, pair.Item2, 1)) { + // Console.WriteLine("missing link " + pair.Item1 + " " + pair.Item2); + // } + // } + // Experiment.printPaths(); + // HotNetsExperiment.impactOfDPThresholdOnGap(); + // Experiment.ImpactNumPathsDemandPinning(); + // Experiment.ImpactNumNodesRadixRandomRegularGraphDemandPinning(); + HotNetsExperiment.impactSmallWordGraphParamsDP(); + // Experiment.ImpactNumPathsPartitionsPop(); + // Experiment.compareGapDelayDiffMethodsPop(); + // Experiment.compareGapDelayDiffMethodsDP(); + // Experiment.compareTopoSizeLatency(); + } + + /// + /// Main entry point for the program. + /// The function takes the command line arguments and stores them in a + /// static instance property of the CliOptions class. + /// It then reads the topology and clusters from the files specified in the + /// command line arguments and then proceeds to find the optimality gap. + /// + /// The arguments. + public static void ssMain(string[] args) + { + // read the command line arguments. + var opts = CommandLine.Parser.Default.ParseArguments(args).MapResult(o => o, e => null); + CliOptions.Instance = opts; + + if (opts == null) + { + Environment.Exit(0); + } + + // read the topology and clusters. + var (topology, clusters) = CliUtils.getTopology(opts.TopologyFile, opts.PathFile, opts.DownScaleFactor, opts.EnableClustering, + opts.NumClusters, opts.ClusterDir, opts.Verbose); + + getSolverAndRunNetwork(topology, clusters); + } + + // TODO: this function is missing proper commenting + private static void getSolverAndRunNetwork(Topology topology, List clusters) + { + var opts = CliOptions.Instance; + // use the Z3 solver via the Zen wrapper library. + switch (opts.SolverChoice) + { + case SolverChoice.Zen: + // run the zen optimizer. + RunNetwork(new SolverZen(), topology, clusters); + break; + case SolverChoice.Gurobi: + var storeProgress = opts.StoreProgress & (opts.Method == MethodChoice.Direct); + if (opts.Heuristic == Heuristic.DemandPinning) + { + RunNetwork(new GurobiSOS(opts.Timeout, Convert.ToInt32(opts.Verbose), + timeToTerminateNoImprovement: opts.TimeToTerminateIfNoImprovement, + numThreads: opts.NumGurobiThreads, + recordProgress: storeProgress, + logPath: opts.LogFile), + topology, clusters); + } + else + { + RunNetwork(new GurobiSOS(opts.Timeout, Convert.ToInt32(opts.Verbose), + timeToTerminateNoImprovement: opts.TimeToTerminateIfNoImprovement, + numThreads: opts.NumGurobiThreads, + recordProgress: storeProgress, + logPath: opts.LogFile), + topology, clusters); + } + break; + default: + throw new Exception("Other solvers are currently invalid."); + } + } + + // TODO: this function is missing proper commenting + private static void RunNetwork(ISolver solver, + Topology topology, List clusters) + { + var opts = CliOptions.Instance; + + // setup the optimal encoder and adversarial input generator. + var optimalEncoder = new TEMaxFlowOptimalEncoder(solver, opts.Paths); + TEAdversarialInputGenerator adversarialInputGenerator; + adversarialInputGenerator = new TEAdversarialInputGenerator(topology, opts.Paths, opts.NumProcesses); + + // setup the heuristic encoder and partitions. + var heuristicSolver = solver; + var (heuristicEncoder, partitioning, partitionList) = CliUtils.getHeuristic(heuristicSolver, topology, opts.Heuristic, opts.Paths, opts.PopSlices, + opts.DemandPinningThreshold * opts.DownScaleFactor, numSamples: opts.NumRandom, partitionSensitivity: opts.PartitionSensitivity, + scaleFactor: opts.DownScaleFactor, InnerEncoding: opts.InnerEncoding, maxShortestPathLen: opts.MaxShortestPathLen); + + // find an adversarial example and show the time taken. + var demandList = new GenericDemandList((opts.DemandList.Split(",")).Select(x => double.Parse(x) * opts.DownScaleFactor).ToHashSet()); + Utils.logger( + string.Format("Demand List:{0}", Newtonsoft.Json.JsonConvert.SerializeObject(demandList.demandList, Newtonsoft.Json.Formatting.Indented)), + opts.Verbose); + var timer = System.Diagnostics.Stopwatch.StartNew(); + Utils.logger("Starting setup", opts.Verbose); + (TEOptimizationSolution, TEOptimizationSolution) result; + switch (opts.Method) + { + case MethodChoice.Direct: + result = CliUtils.getMetaOptResult(adversarialInputGenerator, optimalEncoder, heuristicEncoder, opts.DemandUB, opts.InnerEncoding, + demandList, opts.EnableClustering, opts.ClusterVersion, clusters, opts.NumInterClusterSamples, opts.NumNodesPerCluster, + opts.NumInterClusterQuantizations, opts.Simplify, opts.Verbose, opts.MaxDensity, opts.LargeDemandLB, opts.maxLargeDistance, + opts.maxSmallDistance, false, null); + break; + case MethodChoice.Search: + Utils.logger("Going to use search to find a desirable gap", opts.Verbose); + result = adversarialInputGenerator.FindMaximumGapInterval(optimalEncoder, heuristicEncoder, opts.Confidencelvl, opts.StartingGap, opts.DemandUB, + demandList: demandList); + break; + case MethodChoice.FindFeas: + Utils.logger("Going to find one feasible solution with the specified gap", opts.Verbose); + result = adversarialInputGenerator.FindOptimalityGapAtLeast(optimalEncoder, heuristicEncoder, opts.StartingGap, opts.DemandUB, + demandList: demandList, simplify: opts.Simplify); + break; + case MethodChoice.Random: + Utils.logger("Going to do random search to find some advers inputs", opts.Verbose); + result = adversarialInputGenerator.RandomAdversarialGenerator(optimalEncoder, heuristicEncoder, opts.NumRandom, opts.DemandUB, seed: opts.Seed, + verbose: opts.Verbose, storeProgress: opts.StoreProgress, logPath: opts.LogFile, timeout: opts.Timeout); + break; + case MethodChoice.HillClimber: + Utils.logger("Going to use HillClimber to find some advers inputs", opts.Verbose); + result = adversarialInputGenerator.HillClimbingAdversarialGenerator(optimalEncoder, heuristicEncoder, opts.NumRandom, + opts.NumNeighbors, opts.DemandUB, opts.StdDev, seed: opts.Seed, verbose: opts.Verbose, storeProgress: opts.StoreProgress, + logPath: opts.LogFile, timeout: opts.Timeout); + break; + case MethodChoice.SimulatedAnnealing: + Utils.logger("Going to use Simulated Annealing to find some advers inputs", opts.Verbose); + Utils.logger(opts.LogFile, opts.Verbose); + result = adversarialInputGenerator.SimulatedAnnealing(optimalEncoder, heuristicEncoder, opts.NumRandom, opts.NumNeighbors, + opts.DemandUB, opts.StdDev, opts.InitTmp, opts.TmpDecreaseFactor, seed: opts.Seed, verbose: opts.Verbose, storeProgress: opts.StoreProgress, + logPath: opts.LogFile, timeout: opts.Timeout); + break; + default: + throw new Exception("Wrong Method, please choose between available methods!!"); + } + + if (opts.FullOpt) + { + if (!opts.EnableClustering) + { + throw new Exception("does not need to be enable for non-clustering method"); + } + if (opts.InnerEncoding != InnerRewriteMethodChoice.PrimalDual) + { + throw new Exception("inner encoding should be primal dual"); + } + optimalEncoder.Solver.CleanAll(timeout: opts.FullOptTimer); + var currDemands = new Dictionary<(string, string), double>(result.Item1.Demands); + Utils.setEmptyPairsToZero(topology, currDemands); + result = adversarialInputGenerator.MaximizeOptimalityGap(optimalEncoder, heuristicEncoder, opts.DemandUB, innerEncoding: opts.InnerEncoding, + demandList: demandList, simplify: opts.Simplify, verbose: opts.Verbose, demandInits: currDemands); + optimalEncoder.Solver.CleanAll(focusBstBd: false, timeout: opts.Timeout); + } + + if (opts.UBFocus) + { + var currDemands = new Dictionary<(string, string), double>(result.Item1.Demands); + optimalEncoder.Solver.CleanAll(focusBstBd: true, timeout: opts.UBFocusTimer); + Utils.setEmptyPairsToZero(topology, currDemands); + result = adversarialInputGenerator.MaximizeOptimalityGap(optimalEncoder, heuristicEncoder, opts.DemandUB, innerEncoding: opts.InnerEncoding, + demandList: demandList, simplify: opts.Simplify, verbose: opts.Verbose, demandInits: currDemands); + optimalEncoder.Solver.CleanAll(focusBstBd: false, timeout: opts.Timeout); + } + var optimal = result.Item1.MaxObjective; + var heuristic = result.Item2.MaxObjective; + var demands = new Dictionary<(string, string), double>(result.Item1.Demands); + Utils.setEmptyPairsToZero(topology, demands); + Console.WriteLine("##############################################"); + Console.WriteLine("##############################################"); + Console.WriteLine("##############################################"); + Console.WriteLine($"optimal={optimal}, heuristic={heuristic}, time={timer.ElapsedMilliseconds}ms"); + if (opts.Heuristic == Heuristic.ExpectedPop) + { + CliUtils.findGapExpectedPopAdversarialDemandOnIndependentPartitions(opts, topology, demands, optimal); + } + Console.WriteLine("##############################################"); + Console.WriteLine("##############################################"); + Console.WriteLine("##############################################"); + var optGSolver = new GurobiBinary(); + var optimalEncoderG = new TEMaxFlowOptimalEncoder(optGSolver, maxNumPaths: opts.Paths); + var optZSolver = new SolverZen(); + var optimalEncoderZen = new TEMaxFlowOptimalEncoder, ZenSolution>(optZSolver, maxNumPaths: opts.Paths); + + var gSolver = new GurobiBinary(); + var zSolver = new SolverZen(); + IEncoder heuristicEncoderG; + IEncoder, ZenSolution> heuristicEncoderZ; + switch (opts.Heuristic) + { + case Heuristic.Pop: + Console.WriteLine("Starting exploring pop heuristic"); + heuristicEncoderG = new PopEncoder(gSolver, maxNumPaths: opts.Paths, numPartitions: opts.PopSlices, demandPartitions: partitioning); + heuristicEncoderZ = new PopEncoder, ZenSolution>(zSolver, maxNumPaths: opts.Paths, numPartitions: opts.PopSlices, demandPartitions: partitioning); + break; + case Heuristic.DemandPinning: + Console.WriteLine("Starting exploring demand pinning heuristic"); + heuristicEncoderG = new DirectDemandPinningEncoder(gSolver, k: opts.Paths, threshold: opts.DemandPinningThreshold * opts.DownScaleFactor); + heuristicEncoderZ = new DirectDemandPinningEncoder, ZenSolution>(zSolver, k: opts.Paths, threshold: opts.DemandPinningThreshold * opts.DownScaleFactor); + break; + case Heuristic.ExpectedPop: + Console.WriteLine("Starting to explore expected pop heuristic"); + heuristicEncoderG = new ExpectedPopEncoder(gSolver, k: opts.Paths, numSamples: opts.NumRandom, + numPartitionsPerSample: opts.PopSlices, demandPartitionsList: partitionList); + heuristicEncoderZ = new ExpectedPopEncoder, ZenSolution>(zSolver, k: opts.Paths, numSamples: opts.NumRandom, + numPartitionsPerSample: opts.PopSlices, demandPartitionsList: partitionList); + break; + case Heuristic.PopDp: + throw new Exception("Not Implemented Yet."); + default: + throw new Exception("No heuristic selected."); + } + Utils.checkSolution(topology, heuristicEncoderG, optimalEncoderG, heuristic, optimal, demands, "gurobiCheck"); + } + } +} \ No newline at end of file