public Report GenerateReport(Network net) { Optimization optimal = net.OptimizationResult; Report report = new Report(); report.ReportedNetwork = net; report.UnoptimizedReport = new UnoptimizedSection(); if(optimal != null) { var optimizedReport = new OptimizedSection() { TotalCost = optimal.TotalCost, LinkCosts = new Dictionary<Link, LinkCost>(), RawOptimization = optimal }; foreach(var olink in optimal.Links) { var link = olink.Link; LinkCost lcost = new LinkCost(); lcost.CarFlowCost = olink.Flow * link.Distance * net.CarCostPerMile; lcost.LocomotiveCost = olink.CurrentTrains * link.Distance * (net.FuelCostPerMile + net.NonFuelCostPerMile); optimizedReport.LinkCosts.Add(link, lcost); } report.OptimizedReport = optimizedReport; } return report; }
public Dictionary<Node, Dictionary<Link, int>> TranslateToNodes( Dictionary<string, Dictionary<string, int>> sol, Network net) { var r = new Dictionary<Node, Dictionary<Link, int>>(); foreach(var node in sol) { var nodeActual = net.Nodes.Where(n => n.StationCode == node.Key).First(); r[nodeActual] = new Dictionary<Link,int>(); foreach(var link in node.Value) { string[] nodeNames = link.Key.Split('>'); var linkActual = net.Links.Where( n => n.From.StationCode == nodeNames[0] && n.To.StationCode == nodeNames[1]).First(); r[nodeActual][linkActual] = link.Value; } } return r; }
public Network XmlFileToNetwork(XDocument inFile) { var nodes = new List<Node>(); var links = new List<Link>(); var orders = new List<Order>(); int maxCars = 0; double nonFuelCost = 0, fuelCost = 0, carCost = 0; var nodeCurAdjMap = new Dictionary<Node, double>(); int i =0; foreach (XElement element in inFile.Root.Elements()) { i++; switch (element.Name.ToString()) { case "Network": foreach (XAttribute a in element.Attributes()) { switch (a.Name.ToString()) { case "maxCars": maxCars = int.Parse(a.Value); break; case "nonFuelCost": nonFuelCost = Double.Parse(a.Value); break; case "fuelCost": fuelCost = Double.Parse(a.Value); break; case "carCost": carCost = Double.Parse(a.Value); break; } } foreach (XElement el in element.Elements()) { switch(el.Name.ToString()) { case "Nodes": foreach (XElement e in el.Elements()) { string stationId = "", name = ""; int carCap = 0; //outCapacity = 0; double latitude = 0, longitude = 0; foreach (XAttribute att in e.Attributes()) { switch (att.Name.ToString()) { case "id": stationId = att.Value; break; case "cars": carCap = int.Parse(att.Value); break; case "latitude": latitude = Double.Parse(att.Value); break; case "longitude": longitude = Double.Parse(att.Value); break; } } Node temp = new Node() { Name = name, StationCode = stationId, Location = new Point() { Latitude = latitude, Longitude = longitude }, CarCapacity = carCap, InLinks = new List<Link>(), OutLinks = new List<Link>(), InOrders = new List<Order>(), OutOrders = new List<Order>(), }; nodeCurAdjMap[temp] = GeocodingEngine.getInstance() .LocationConvertToUSD(temp.Location, 1.0); nodes.Add(temp); } break; //end Nodes case "Arcs": foreach (XElement e in el.Elements()) { string to ="", from =""; double track_mult = 0, fuel_adj =0; int max_trains = 0; foreach (XAttribute att in e.Attributes()) { switch (att.Name.ToString()) { case "end": to = att.Value; break; case "start": from = att.Value; break; case "trackMultiplier": track_mult = Double.Parse(att.Value); break; case "maxTrains": max_trains = Int32.Parse(att.Value); break; case "fuelAdj": fuel_adj = Double.Parse(att.Value); break; } } int toIndex = -1; int fromIndex = -1; int k = 0; foreach (Node n in nodes) { if (toIndex >= 0 && fromIndex >= 0) break; if (n.StationCode.Equals(to)) toIndex = k; if (n.StationCode.Equals(from)) fromIndex = k; k++; } Link tempLink = new Link { From = nodes[fromIndex], To = nodes[toIndex], MaxTrains = max_trains, FuelAdjustment = fuel_adj, }; tempLink.Distance = GeocodingEngine.getInstance().Distance( tempLink.From.Location, tempLink.To.Location ) * track_mult; nodes[fromIndex].OutLinks.Add(tempLink); nodes[toIndex].InLinks.Add(tempLink); links.Add(tempLink); } break; //end arcs } } break; //end network case "Orders": foreach (XElement el in element.Elements()) { string id ="", origin ="", dest =""; int cars = 0; double revenue = 0; foreach (XAttribute att in el.Attributes()) { switch(att.Name.ToString()) { case "cars": cars = int.Parse(att.Value); break; case "revenue": revenue = Double.Parse(att.Value); break; case "to": dest = att.Value; break; case "from": origin = att.Value; break; } } int destIndex = -1; int origIndex = -1; int k = 0; foreach (Node n in nodes) { if (destIndex >= 0 && origIndex >= 0) break; if(n.StationCode.Equals(dest)) destIndex = k; if(n.StationCode.Equals(origin)) origIndex = k; k++; } Order tempOrder = new Order { Cars = cars, Destination = nodes[destIndex], Origin = nodes[origIndex], //XMLOrderID = }; tempOrder.Revenue = nodeCurAdjMap[tempOrder.Origin] * revenue; nodes[origIndex].OutOrders.Add(tempOrder); nodes[destIndex].InOrders.Add(tempOrder); orders.Add(tempOrder); } break; //end orders } } var network = new Network{ CarCostPerMile = carCost, FuelCostPerMile = fuelCost, NonFuelCostPerMile = nonFuelCost, MaxCarsPerTrain = maxCars, Links = links, Nodes = nodes, Orders = orders, OptimizationResult = null}; return network; }
/// <summary> /// Clones this network, creating new nodes, links, orders, etc. /// Does NOT copy the ID /// </summary> public object Clone() { Network net = new Network() { Author = Author, MaxCarsPerTrain = MaxCarsPerTrain, NonFuelCostPerMile = NonFuelCostPerMile, FuelCostPerMile = FuelCostPerMile, CarCostPerMile = CarCostPerMile, }; if(OptimizationResult != null) { net.OptimizationResult = (Optimization)OptimizationResult.Clone(); net.OptimizationResult.OptimizedNetwork = net; } // Copying nodes a bit more complicated so do it outside primitive cloning. // Make a mapping for old nodes to new nodes, for easy translation on links and orders. var oldToNewNodes = new Dictionary<Node, Node>(); var oldToNewLinks = new Dictionary<Link, Link>(); net.Nodes = Nodes.Select(n => { Node newN = (Node)n.Clone(); oldToNewNodes.Add(n, newN); return newN; }).ToList(); net.Links = Links.Select(l => { Link newL = (Link)l.Clone(); newL.From = oldToNewNodes[l.From]; newL.To = oldToNewNodes[l.To]; oldToNewNodes[l.From].OutLinks.Add(newL); oldToNewNodes[l.To].InLinks.Add(newL); oldToNewLinks.Add(l, newL); return newL; }).ToList(); net.Orders = Orders.Select(l => { Order newO = (Order)l.Clone(); newO.Origin = oldToNewNodes[l.Origin]; newO.Destination = oldToNewNodes[l.Destination]; oldToNewNodes[l.Origin].OutOrders.Add(newO); oldToNewNodes[l.Destination].InOrders.Add(newO); return newO; }).ToList(); // Copy over the cloned optimization if it exists. if(OptimizationResult != null) { net.OptimizationResult = (Optimization)OptimizationResult.Clone(); net.OptimizationResult.Nodes = OptimizationResult.Nodes.Select(n => { NodeOptimized newNO = (NodeOptimized)n.Clone(); newNO.Node = oldToNewNodes[n.Node]; return newNO; }).ToList(); net.OptimizationResult.Links = OptimizationResult.Links.Select(l => { LinkOptimized newLO = (LinkOptimized)l.Clone(); newLO.Link = oldToNewLinks[l.Link]; return newLO; }).ToList(); } return net; }
public ActionResult CreateBlank(string NetworkName) { var nvm = new NetworkListViewModel(); if(NetworkName == "") { ViewBag.UploadAlert = "Enter a network name"; using (var c = new DataModelContext()) { nvm.Networks = c.Networks.Include("Author").Where(n => n.Name != null).ToList(); } return View("Index", nvm); } using(var c = new DataModelContext()) { var xmlnetwork = new Network(); xmlnetwork.Name = NetworkName; xmlnetwork.Author = UserDataEngine.getInstance().GetCurrentUser(c, HttpContext); xmlnetwork.LastEdit = DateTime.Now; c.Networks.Add(xmlnetwork); try { c.SaveChanges(); } catch(DbEntityValidationException e) { foreach(var i in e.EntityValidationErrors) { Console.WriteLine(i.ValidationErrors); } throw e; } nvm.Networks = c.Networks.Include("Author").Where(n => n.Name != null).ToList(); ViewBag.NewNetworkID = xmlnetwork.ID; } ViewBag.Alert = "Network upload successful"; ViewBag.AlertClass = "alert-success"; return View("Index", nvm); }
public RawOptimizedState Optimize( Network net, OptimizationOptions options) { var context = SolverContext.GetContext(); context.ClearModel(); var model = context.CreateModel(); // Load in nodes, links, orders as lists. var nodes = net.Nodes.ToList(); var links = net.Links.ToList(); var orders = net.Orders.ToList(); // Make sure the existing optimization has default weights set. if(options.AllowLocomotiveCapacityExpansion) { if(net.OptimizationResult.DefaultLinkExpansion == null) { net.OptimizationResult.DefaultLinkExpansion = new ExpansionParameters() { CapacityExpansionMaxPossible = 100, CapacityExpansionCostPerUnit = 10000 }; } } if(options.AllowNodeCapacityExpansion) { if(net.OptimizationResult.DefaultNodeExpansion == null) { net.OptimizationResult.DefaultNodeExpansion = new ExpansionParameters() { CapacityExpansionMaxPossible = 10000, CapacityExpansionCostPerUnit = 1000 }; } } #region Decision variables, car costs var flowDecisions = new Dictionary<Node,Dictionary<Link,Decision>>(); Term totalCarCosts = 0; var expandNodeDecisions = new Dictionary<NodeOptimized,Decision>(); var expandLinkDecisions = new Dictionary<LinkOptimized,Decision>(); Term totalExpandCosts = 0; // One per link for each node. foreach(var node in nodes) { flowDecisions[node] = new Dictionary<Link,Decision>(); foreach(var link in links) { flowDecisions[node][link] = new Decision( Domain.IntegerNonnegative, "Node_" + node.ID + "_cars_" + link.From.ID + "_to_" + link.To.ID ); model.AddDecision(flowDecisions[node][link]); // Car cost for this link. totalCarCosts += flowDecisions[node][link] * link.Distance * net.CarCostPerMile; } } // Create the decision vars for expansion, if necessary. if(options.AllowNodeCapacityExpansion) { foreach(var node in net.OptimizationResult.Nodes) { expandNodeDecisions[node] = new Decision( Domain.IntegerNonnegative, "Node_" + node.Node.ID + "_expansion" ); model.AddDecision(expandNodeDecisions[node]); if(node.Expansion == null) node.Expansion = new ExpansionParameters(); var perUnitCost = node.Expansion.CapacityExpansionCostPerUnit ?? (int)net.OptimizationResult.DefaultNodeExpansion.CapacityExpansionCostPerUnit; int maxPossible = node.Expansion.CapacityExpansionMaxPossible ?? (int)net.OptimizationResult.DefaultNodeExpansion.CapacityExpansionMaxPossible; totalExpandCosts += expandNodeDecisions[node] * perUnitCost; model.AddConstraint("Node_exp_cap_" + node.Node.ID, expandNodeDecisions[node] <= maxPossible); } } if(options.AllowLocomotiveCapacityExpansion) { foreach(var link in net.OptimizationResult.Links) { expandLinkDecisions[link] = new Decision( Domain.IntegerNonnegative, "Link_" + link.Link.ID + "_expansion" ); model.AddDecision(expandLinkDecisions[link]); if(link.Expansion == null) link.Expansion = new ExpansionParameters(); var perUnitCost = link.Expansion.CapacityExpansionCostPerUnit ?? (int)net.OptimizationResult.DefaultLinkExpansion.CapacityExpansionCostPerUnit; int maxPossible = link.Expansion.CapacityExpansionMaxPossible ?? (int)net.OptimizationResult.DefaultLinkExpansion.CapacityExpansionMaxPossible; totalExpandCosts += expandLinkDecisions[link] * perUnitCost; model.AddConstraint("Link_exp_cap_" + link.Link.ID, expandLinkDecisions[link] <= maxPossible); } } #endregion #region Locomotive decision variables, locomotive costs Term totalLocomotiveCosts = 0; var locomotiveDecisions = new Dictionary<Link, Decision>(); foreach(var link in links) { locomotiveDecisions[link] = new Decision(Domain.IntegerNonnegative, "Locomotives_" + link.From.ID + "_to_" + link.To.ID); model.AddDecision(locomotiveDecisions[link]); // Constraint on max locomotives. // If optimized, number of locomotives is allowed to be below the capacity. Term locos = locomotiveDecisions[link]; if(options.AllowLocomotiveCapacityExpansion) { Decision expand = expandLinkDecisions.FirstOrDefault( l => l.Key.Link == link ).Value; model.AddConstraint("Maxloco_" + link.From.ID + "_to_" + link.To.ID, locos <= link.MaxTrains + expand); } else { model.AddConstraint("Maxloco_" + link.From.ID + "_to_" + link.To.ID, locos <= link.MaxTrains); } Term locoCost = link.Distance * locomotiveDecisions[link] * (net.NonFuelCostPerMile + net.FuelCostPerMile * link.FuelAdjustment); totalLocomotiveCosts += locoCost; } #endregion #region Constraint 1: Order Flow // For loop so that indices are kept. foreach(var fulfiller in nodes) { foreach(var fulfilled in nodes) { var fulfilledOrders = orders.Where(o => o.Origin == fulfiller); if(fulfilled != fulfiller) fulfilledOrders = fulfilledOrders.Where(o => o.Destination == fulfilled); // Required flow on nodeFulfilled to satisfy the order. int requiredFlow = 0; foreach(Order order in fulfilledOrders) { if(fulfilled == fulfiller) requiredFlow += order.Cars; else requiredFlow -= order.Cars; } // Sum of decision variables that should match the required flow. Term actualFlow = 0; // Get links that go to and from fulfilled. var fromLinks = links.Where(l => l.From == fulfilled); var toLinks = links.Where(l => l.To == fulfilled); // Go through related decision variables. foreach(var link in fromLinks) { // Decision variable index. actualFlow += flowDecisions[fulfiller][link]; } foreach(var link in toLinks) { // Decision variable index. actualFlow -= flowDecisions[fulfiller][link]; } model.AddConstraint("_" + fulfiller.ID + "_fulfilling_" + fulfilled.ID, actualFlow == requiredFlow); } } #endregion #region Constraint 2: Link Capacity foreach(var link in links) { // Total amount of flow over this link, summed for all car sources. Term totalFlow = 0; foreach(var node in nodes) { totalFlow += flowDecisions[node][link]; } Term linkCapacity = locomotiveDecisions[link] * net.MaxCarsPerTrain; if(options.AllowLocomotiveCapacityExpansion) { LinkOptimized optNode = net.OptimizationResult.Links.Where(n => n.Link == link).First(); // If the user has set max possible expansions for flow in or out, use them. model.AddConstraint("Link_" + link.ID + "_cap", totalFlow <= linkCapacity + expandLinkDecisions[optNode]); } else { model.AddConstraint("Link_" + link.ID + "_cap", totalFlow <= linkCapacity); } } #endregion #region Constraint 3/4: Node Capacity foreach(var node in nodes) { // Get all in and out links. var outLinks = links.Where(l => l.From == node); var inLinks = links.Where(l => l.To == node); Term flowOut = 0; Term flowIn = 0; foreach(var nodeo in nodes) { foreach(var link in outLinks) flowOut += flowDecisions[nodeo][link]; foreach(var link in inLinks) flowIn += flowDecisions[nodeo][link]; } if(options.AllowNodeCapacityExpansion) { NodeOptimized optNode = net.OptimizationResult.Nodes.Where(n => n.Node == node).First(); // If the user has set max possible expansions for flow in or out, use them. model.AddConstraint("Node_" + node.ID + "_capacity_out", flowOut <= node.CarCapacity + expandNodeDecisions[optNode]); model.AddConstraint("Node_" + node.ID + "_capacity_in", flowIn <= node.CarCapacity + expandNodeDecisions[optNode]); // If there is no max, make no constraint. } else { model.AddConstraint("Node_" + node.ID + "_capacity_out", flowOut <= node.CarCapacity); model.AddConstraint("Node_" + node.ID + "_capacity_in", flowIn <= node.CarCapacity); } } #endregion #region Objective Function // Objective function. (Minimize) Term totalCost = totalCarCosts + totalLocomotiveCosts + totalExpandCosts; model.AddGoal("TotalCost", GoalKind.Minimize, totalCost); #endregion //Directive directive = new LpSolveDirective(); //Directive directive = new SimplexDirective(); Directive directive = new GurobiDirective(); //Directive directive = new MixedIntegerProgrammingDirective(); directive.TimeLimit = TIMEOUT_MAX_SECONDS * 1000; var solution = context.Solve(directive); var extractedFlowDec = new Dictionary<Node, Dictionary<Link, int>>(); var extractedLocoDec = new Dictionary<Link, int>(); foreach(var nvp in flowDecisions) { extractedFlowDec[nvp.Key] = new Dictionary<Link,int>(); foreach(var lvp in nvp.Value) { extractedFlowDec[nvp.Key][lvp.Key] = (int)lvp.Value.ToDouble(); } } foreach(var lvp in locomotiveDecisions) { extractedLocoDec[lvp.Key] = (int)lvp.Value.ToDouble(); } foreach(var nvp in expandNodeDecisions) { nvp.Key.ExpansionSuggested = (int)nvp.Value.ToDouble(); } foreach(var lvp in expandLinkDecisions) { lvp.Key.ExpansionSuggested = (int)lvp.Value.ToDouble(); } // Extract the suggestion cost. (Don't know a better way to do this.) double suggestionCapitalCost = 0.0; if(options.AllowNodeCapacityExpansion) { foreach(var node in net.OptimizationResult.Nodes) { var perUnitCost = node.Expansion.CapacityExpansionCostPerUnit ?? (int)net.OptimizationResult.DefaultNodeExpansion.CapacityExpansionCostPerUnit; suggestionCapitalCost += expandNodeDecisions[node].ToDouble() * perUnitCost; } } if(options.AllowLocomotiveCapacityExpansion) { foreach(var link in net.OptimizationResult.Links) { var perUnitCost = link.Expansion.CapacityExpansionCostPerUnit ?? (int)net.OptimizationResult.DefaultLinkExpansion.CapacityExpansionCostPerUnit; suggestionCapitalCost += expandLinkDecisions[link].ToDouble() * perUnitCost; } } RawOptimizedState.RawQuality q; switch(solution.Quality) { case SolverQuality.Optimal: q = RawOptimizedState.RawQuality.Solved; break; case SolverQuality.Infeasible: q = RawOptimizedState.RawQuality.Infeasible; break; case SolverQuality.InfeasibleOrUnbounded: case SolverQuality.Unbounded: q = RawOptimizedState.RawQuality.Unbounded; break; case SolverQuality.Unknown: case SolverQuality.Feasible: default: q = RawOptimizedState.RawQuality.TimedOut; break; } var rawState = new RawOptimizedState() { solvedNetwork = net, flowDecisions = extractedFlowDec, locomotiveDecisions = extractedLocoDec, totalCost = (int)(model.Goals.First().ToDouble() - suggestionCapitalCost), suggestionCapitalCost = (int)suggestionCapitalCost, Quality = q }; return rawState; }