Free cookie consent management tool by TermsFeed Policy Generator

source: branches/VRP/HeuristicLab.Problems.VehicleRouting/3.3/TSPLIBParser.cs @ 4232

Last change on this file since 4232 was 4232, checked in by svonolfe, 14 years ago

Added TSPLIBParser (#1039)

File size: 10.2 KB
Line 
1#region License Information
2/* HeuristicLab
3 * Copyright (C) 2002-2010 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
4 *
5 * This file is part of HeuristicLab.
6 *
7 * HeuristicLab is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * HeuristicLab is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
19 */
20#endregion
21
22using System;
23using System.Globalization;
24using System.IO;
25
26namespace HeuristicLab.Problems.VehicleRouting {
27  /// <summary>
28  /// Parses a *.tsp file in TSPLIB format and extracts its information about a TSP.
29  /// </summary>
30  public class TSPLIBParser {
31    #region Inner Enum TSPLIBEdgeWeightType
32    public enum TSPLIBEdgeWeightType {
33      UNDEFINED,
34      EUC_2D,
35      GEO
36    }
37    #endregion
38
39    private const int EOF = 0;
40    private const int NAME = 1;
41    private const int TYPE = 2;
42    private const int COMMENT = 3;
43    private const int DIM = 4;
44    private const int WEIGHTTYPE = 5;
45    private const int NODETYPE = 6;
46    private const int NODESECTION = 7;
47    private const int CAPACITY = 8;
48    private const int DEPOTSECTION = 9;
49    private const int DEMANDSECTION = 10;
50
51    private StreamReader source;
52
53    private string name;
54    /// <summary>
55    /// Gets the name of the parsed TSP.
56    /// </summary>
57    public string Name {
58      get { return name; }
59    }
60    private string comment;
61    /// <summary>
62    /// Gets the comment of the parsed TSP.
63    /// </summary>
64    public string Comment {
65      get { return comment; }
66    }
67    private double[,] vertices;
68    /// <summary>
69    /// Gets the vertices of the parsed TSP.
70    /// </summary>
71    public double[,] Vertices {
72      get { return vertices; }
73    }
74    private TSPLIBEdgeWeightType weightType;
75    /// <summary>
76    /// Gets the weight type of the parsed TSP.
77    /// </summary>
78    public TSPLIBEdgeWeightType WeightType {
79      get { return weightType; }
80    }
81
82    private double capacity;
83    public double Capacity {
84      get {
85        return capacity;
86      }
87    }
88
89    private int depot;
90    public int Depot {
91      get {
92        return depot;
93      }
94    }
95
96    private double[] demands;
97    public double[] Demands {
98      get {
99        return demands;
100      }
101    }
102
103    /// <summary>
104    /// Initializes a new instance of <see cref="TSPLIBParser"/> with the given <paramref name="path"/>.
105    /// </summary>
106    /// <exception cref="ArgumentException">Thrown if the input file is not a TSPLIB TSP file (*.tsp)
107    /// </exception>
108    /// <param name="path">The path where the TSP is stored.</param>
109    public TSPLIBParser(String path) {
110      if (!path.EndsWith(".vrp"))
111        throw new ArgumentException("Input file has to be a TSPLIB CVRP file (*.tsp).");
112
113      source = new StreamReader(path);
114      name = path;
115      comment = string.Empty;
116      vertices = null;
117      weightType = TSPLIBEdgeWeightType.UNDEFINED;
118      capacity = -1;
119      depot = -1;
120      demands = null;
121    }
122
123    /// <summary>
124    /// Reads the TSPLIB TSP file and parses the elements.
125    /// </summary>
126    /// <exception cref="InvalidDataException">Thrown if the file has an invalid format or contains invalid data.</exception>
127    public void Parse() {
128      int section = -1;
129      string str = null;
130      bool typeIsChecked = false;
131      bool weightTypeIsChecked = false;
132
133      do {
134        str = source.ReadLine();
135        section = GetSection(str);
136
137        if (section != -1) {
138          switch (section) {
139            case NAME:
140              ReadName(str);
141              break;
142            case TYPE:
143              CheckType(str);
144              typeIsChecked = true;
145              break;
146            case COMMENT:
147              ReadComment(str);
148              break;
149            case DIM:
150              InitArrays(str);
151              break;
152            case WEIGHTTYPE:
153              ReadWeightType(str);
154              weightTypeIsChecked = true;
155              break;
156            case NODETYPE:
157              CheckNodeType(str);
158              break;
159            case NODESECTION:
160              ReadVertices();
161              break;
162            case CAPACITY:
163              ReadCapacity(str);
164              break;
165            case DEPOTSECTION:
166              ReadDepot();
167              break;
168            case DEMANDSECTION:
169              ReadDemands();
170              break;
171          }
172        }
173      } while (!((section == EOF) || (str == null)));
174
175      if (!(typeIsChecked && weightTypeIsChecked))
176        throw new InvalidDataException("Input file does not contain type or edge weight type information.");
177    }
178
179    private int GetSection(string str) {
180      if (str == null)
181        return EOF;
182
183      string[] tokens = str.Split(new string[] { ":" }, StringSplitOptions.None);
184      if (tokens.Length == 0)
185        return -1;
186
187      string token = tokens[0].Trim();
188      if (token.Equals("eof", StringComparison.OrdinalIgnoreCase))
189        return EOF;
190      if (token.Equals("name", StringComparison.OrdinalIgnoreCase))
191        return NAME;
192      if (token.Equals("type", StringComparison.OrdinalIgnoreCase))
193        return TYPE;
194      if (token.Equals("comment", StringComparison.OrdinalIgnoreCase))
195        return COMMENT;
196      if (token.Equals("dimension", StringComparison.OrdinalIgnoreCase))
197        return DIM;
198      if (token.Equals("edge_weight_type", StringComparison.OrdinalIgnoreCase))
199        return WEIGHTTYPE;
200      if (token.Equals("node_coord_type", StringComparison.OrdinalIgnoreCase))
201        return NODETYPE;
202      if (token.Equals("node_coord_section", StringComparison.OrdinalIgnoreCase))
203        return NODESECTION;
204      if (token.Equals("capacity", StringComparison.OrdinalIgnoreCase))
205        return CAPACITY;
206      if (token.Equals("depot_section", StringComparison.OrdinalIgnoreCase))
207        return DEPOTSECTION;
208      if (token.Equals("demand_section", StringComparison.OrdinalIgnoreCase))
209        return DEMANDSECTION;
210
211      return -1;
212    }
213
214    private void ReadName(string str) {
215      string[] tokens = str.Split(new string[] { ":" }, StringSplitOptions.None);
216      name = tokens[tokens.Length - 1].Trim();
217    }
218
219    private void CheckType(string str) {
220      string[] tokens = str.Split(new string[] { ":" }, StringSplitOptions.None);
221
222      string type = tokens[tokens.Length - 1].Trim();
223      if (!type.Equals("cvrp", StringComparison.OrdinalIgnoreCase))
224        throw new InvalidDataException("Input file type is not \"CVRP\"");
225    }
226
227    private void ReadComment(string str) {
228      string[] tokens = str.Split(new string[] { ":" }, StringSplitOptions.None);
229      comment = tokens[tokens.Length - 1].Trim();
230    }
231
232    private void InitArrays(string str) {
233      string[] tokens = str.Split(new string[] { ":" }, StringSplitOptions.None);
234      string dimension = tokens[tokens.Length - 1].Trim();
235
236      int dim = Int32.Parse(dimension);
237      vertices = new double[dim, 2];
238      demands = new double[dim];
239    }
240
241    private void ReadWeightType(string str) {
242      string[] tokens = str.Split(new string[] { ":" }, StringSplitOptions.None);
243      string type = tokens[tokens.Length - 1].Trim();
244
245      if (type.Equals("euc_2d", StringComparison.OrdinalIgnoreCase))
246        weightType = TSPLIBEdgeWeightType.EUC_2D;
247      else if (type.Equals("geo", StringComparison.OrdinalIgnoreCase))
248        weightType = TSPLIBEdgeWeightType.GEO;
249      else
250        throw new InvalidDataException("Input file contains an unsupported edge weight type (only \"EUC_2D\" and \"GEO\" are supported).");
251    }
252
253    private void CheckNodeType(string str) {
254      string[] tokens = str.Split(new string[] { ":" }, StringSplitOptions.None);
255      string type = tokens[tokens.Length - 1].Trim();
256
257      if (!type.Equals("twod_coords", StringComparison.OrdinalIgnoreCase))
258        throw new InvalidDataException("Input file contains an unsupported node coordinates type (only \"TWOD_COORDS\" is supported).");
259    }
260
261    private void ReadVertices() {
262      if (vertices == null)
263        throw new InvalidDataException("Input file does not contain dimension information.");
264
265      for (int i = 0; i < (vertices.Length / 2); i++) {
266        string str = source.ReadLine();
267        string[] tokens = str.Split(new string[] { " " }, StringSplitOptions.RemoveEmptyEntries);
268
269        if (tokens.Length != 3)
270          throw new InvalidDataException("Input file contains invalid node coordinates.");
271
272        CultureInfo culture = new CultureInfo("en-US");
273        vertices[i, 0] = double.Parse(tokens[1], culture.NumberFormat);
274        vertices[i, 1] = double.Parse(tokens[2], culture.NumberFormat);
275      }
276    }
277
278    private void ReadCapacity(string str) {
279      string[] tokens = str.Split(new string[] { ":" }, StringSplitOptions.None);
280      capacity = double.Parse(tokens[tokens.Length - 1].Trim());
281    }
282
283    private void ReadDepot() {
284      int read;
285
286      do {
287        string str = source.ReadLine();
288        read = int.Parse(str);
289        if (read != -1)
290          depot = read;
291      } while (read != -1);
292    }
293
294    private void ReadDemands() {
295      if (demands == null)
296        throw new InvalidDataException("Input file does not contain dimension information.");
297
298      for (int i = 0; i < demands.Length; i++) {
299        string str = source.ReadLine();
300        string[] tokens = str.Split(new string[] { " " }, StringSplitOptions.RemoveEmptyEntries);
301
302        if (tokens.Length != 2)
303          throw new InvalidDataException("Input file contains invalid demands.");
304
305        int index = int.Parse(tokens[0]);
306        double value = double.Parse(tokens[1]);
307
308        demands[index - 1] = value;
309      }
310    }
311  }
312}
Note: See TracBrowser for help on using the repository browser.