Business Objects & Instantiation
Node (Business Object)
For convenience we use an implementation with nodes and edges even if the nodes only have a name.
Node.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace NDP
{
/// <summary>
/// A node of the network
/// </summary>
public class Node : INode
{
/// <summary>
/// Initializes a new instance of the <see cref="Node"/> class, representing a node of the network
/// </summary>
/// <param name="name">
/// Name of the node
/// </param>
/// <param name="demand">
/// The demand of this node. A negative demand is supply.
/// </param>
public Node(string name, double demand)
{
this.Name = name;
this.Demand = demand;
}
/// <summary>
/// Gets the demand of this node. A negative demand is supply.
/// </summary>
public double Demand { get; }
/// <summary>
/// Gets the name of the node
/// </summary>
public string Name { get; }
/// <summary>
/// Name of the node
/// </summary>
/// <returns>
/// The name of this node (<see cref="string"/>).
/// </returns>
public override string ToString() => this.Name;
}
}
Edge (Business Object)
Each edge connects exactly two nodes and has a set distance.
Edge.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace NDP
{
/// <summary>
/// An edge between two nodes of the network.
/// </summary>
public class Edge : IEdge
{
/// <summary>
/// Initializes a new instance of the <see cref="Edge"/> class.
/// </summary>
/// <param name="fromNode">
/// The departing node
/// </param>
/// <param name="toNode">
/// The arrival node
/// </param>
/// <param name="capacity">
/// The capacity of the edge
/// </param>
/// <param name="costPerFlowUnit">
/// The cost per flow unit
/// </param>
/// <param name="designCost">
/// The design cost, applied if the edge is used at all.
/// </param>
public Edge(INode fromNode, INode toNode, double? capacity, double costPerFlowUnit, double designCost)
{
// set the parameter information
this.FromNode = fromNode;
this.ToNode = toNode;
this.Capacity = capacity;
this.CostPerFlowUnit = costPerFlowUnit;
this.DesignCost = designCost;
}
/// <summary>
/// Gets the design cost of this edge, which are applied if the edge is used at all.
/// </summary>
public double DesignCost { get; }
/// <summary>
/// Gets the cost per flow unit on this edge
/// </summary>
public double CostPerFlowUnit { get; }
/// <summary>
/// Gets the capacity of the edge
/// </summary>
public double? Capacity { get; }
/// <summary>
/// Gets the end node of the edge
/// </summary>
public INode ToNode { get; }
/// <summary>
/// Gets the start node of the edge
/// </summary>
public INode FromNode { get; }
/// <summary>
/// Gets or sets a value indicating whether the edge is used in the solution
/// </summary>
public bool IsUsed { get; set; }
/// <summary>
/// Gets or sets the flow of the optimal solution
/// </summary>
public double Flow { get; set; }
/// <summary>
/// Name of the edge
/// </summary>
/// <returns>
/// The <see cref="string"/>.
/// </returns>
public override string ToString() => $"Edge {this.FromNode} to {this.ToNode}";
}
}
Program instance
Instantiation of the different Nodes and Edges, the Model, as well as the Solver and an empty Solution.
Program.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using OPTANO.Modeling.Optimization.Solver.Gurobi810;
namespace NDP
{
using OPTANO.Modeling.Common;
using OPTANO.Modeling.Optimization;
using OPTANO.Modeling.Optimization.Configuration;
using OPTANO.Modeling.Optimization.Solver.Gurobi810;
/// <summary>
/// Demo program solving a network design problem
/// </summary>
class Program
{
/// <summary>
/// The main method
/// </summary>
/// <param name="args">
/// no arguments required
/// </param>
static void Main(string[] args)
{
// create example Nodes
INode pb = new Node("Paderborn", 50);
INode ny = new Node("New York", -100);
INode b = new Node("Beijing", 50);
INode sp = new Node("São Paulo", 50);
INode sf = new Node("San Francisco", -50);
// assign these nodes to a list of INodes
var nodes = new List<INode> { pb, ny, b, sp, sf };
// create example Edges
IEdge one = new Edge(ny, pb, null, 3, 6100);
IEdge two = new Edge(b, ny, null, 2, 11000);
IEdge three = new Edge(sp, b, 75, 1, 17600);
IEdge four = new Edge(sf, sp, null, 4, 10500);
IEdge five = new Edge(sp, pb, null, 5, 9900);
IEdge six = new Edge(pb, b, 50, 1, 7600);
// assign these edges to a list of IEdges
var edges = new List<IEdge> { one, two, three, four, five, six };
// Use long names for easier debugging/model understanding.
var config = new Configuration();
config.NameHandling = NameHandlingStyle.UniqueLongNames;
config.ComputeRemovedVariables = true;
using (var scope = new ModelScope(config))
{
// create a model, based on given data and the model scope
var designModel = new NetworkDesignModel(nodes, edges);
// Get a solver instance, change your solver
using (var solver = new GurobiSolver())
{
// solve the model
var solution = solver.Solve(designModel.Model);
// import the results back into the model
designModel.Model.VariableCollections.ForEach(vc => vc.SetVariableValues(solution.VariableValues));
// print objective and variable decisions
Console.WriteLine($"{solution.ObjectiveValues.Single()}");
designModel.x.Variables.ForEach(x => Console.WriteLine($"{x.ToString().PadRight(36)}: {x.Value}"));
designModel.y.Variables.ForEach(y => Console.WriteLine($"{y.ToString().PadRight(36)}: {y.Value}"));
designModel.Model.VariableStatistics.WriteCSV(AppDomain.CurrentDomain.BaseDirectory);
Console.ReadLine();
}
}
}
}
}
Next Step
- Creating the Model