Free cookie consent management tool by TermsFeed Policy Generator

source: tags/3.3.7/HeuristicLab.Problems.TravelingSalesman/3.3/TravelingSalesmanProblem.cs @ 15470

Last change on this file since 15470 was 8221, checked in by abeham, 12 years ago

#1396: fixed loading errors when switching from coordinates to distance matrix evaluation

File size: 21.8 KB
Line 
1#region License Information
2/* HeuristicLab
3 * Copyright (C) 2002-2012 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.Collections.Generic;
24using System.IO;
25using System.Linq;
26using HeuristicLab.Common;
27using HeuristicLab.Core;
28using HeuristicLab.Data;
29using HeuristicLab.Encodings.PermutationEncoding;
30using HeuristicLab.Optimization;
31using HeuristicLab.Parameters;
32using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
33using HeuristicLab.PluginInfrastructure;
34using HeuristicLab.Problems.Instances;
35
36namespace HeuristicLab.Problems.TravelingSalesman {
37  [Item("Traveling Salesman Problem", "Represents a symmetric Traveling Salesman Problem.")]
38  [Creatable("Problems")]
39  [StorableClass]
40  public sealed class TravelingSalesmanProblem : SingleObjectiveHeuristicOptimizationProblem<ITSPEvaluator, IPermutationCreator>, IStorableContent,
41    IProblemInstanceConsumer<TSPData> {
42    private static readonly int DistanceMatrixSizeLimit = 1000;
43    public string Filename { get; set; }
44
45    #region Parameter Properties
46    public OptionalValueParameter<DoubleMatrix> CoordinatesParameter {
47      get { return (OptionalValueParameter<DoubleMatrix>)Parameters["Coordinates"]; }
48    }
49    public OptionalValueParameter<DistanceMatrix> DistanceMatrixParameter {
50      get { return (OptionalValueParameter<DistanceMatrix>)Parameters["DistanceMatrix"]; }
51    }
52    public ValueParameter<BoolValue> UseDistanceMatrixParameter {
53      get { return (ValueParameter<BoolValue>)Parameters["UseDistanceMatrix"]; }
54    }
55    public OptionalValueParameter<Permutation> BestKnownSolutionParameter {
56      get { return (OptionalValueParameter<Permutation>)Parameters["BestKnownSolution"]; }
57    }
58    #endregion
59
60    #region Properties
61    public DoubleMatrix Coordinates {
62      get { return CoordinatesParameter.Value; }
63      set { CoordinatesParameter.Value = value; }
64    }
65    public DistanceMatrix DistanceMatrix {
66      get { return DistanceMatrixParameter.Value; }
67      set { DistanceMatrixParameter.Value = value; }
68    }
69    public BoolValue UseDistanceMatrix {
70      get { return UseDistanceMatrixParameter.Value; }
71      set { UseDistanceMatrixParameter.Value = value; }
72    }
73    public Permutation BestKnownSolution {
74      get { return BestKnownSolutionParameter.Value; }
75      set { BestKnownSolutionParameter.Value = value; }
76    }
77    private BestTSPSolutionAnalyzer BestTSPSolutionAnalyzer {
78      get { return Operators.OfType<BestTSPSolutionAnalyzer>().FirstOrDefault(); }
79    }
80    private TSPAlleleFrequencyAnalyzer TSPAlleleFrequencyAnalyzer {
81      get { return Operators.OfType<TSPAlleleFrequencyAnalyzer>().FirstOrDefault(); }
82    }
83    private TSPPopulationDiversityAnalyzer TSPPopulationDiversityAnalyzer {
84      get { return Operators.OfType<TSPPopulationDiversityAnalyzer>().FirstOrDefault(); }
85    }
86    #endregion
87
88    // BackwardsCompatibility3.3
89    #region Backwards compatible code, remove with 3.4
90    [Obsolete]
91    [Storable(Name = "operators")]
92    private IEnumerable<IOperator> oldOperators {
93      get { return null; }
94      set {
95        if (value != null && value.Any())
96          Operators.AddRange(value);
97      }
98    }
99    #endregion
100
101    [StorableConstructor]
102    private TravelingSalesmanProblem(bool deserializing) : base(deserializing) { }
103    private TravelingSalesmanProblem(TravelingSalesmanProblem original, Cloner cloner)
104      : base(original, cloner) {
105      RegisterEventHandlers();
106    }
107    public override IDeepCloneable Clone(Cloner cloner) {
108      return new TravelingSalesmanProblem(this, cloner);
109    }
110    public TravelingSalesmanProblem()
111      : base(new TSPRoundedEuclideanPathEvaluator(), new RandomPermutationCreator()) {
112      Parameters.Add(new OptionalValueParameter<DoubleMatrix>("Coordinates", "The x- and y-Coordinates of the cities."));
113      Parameters.Add(new OptionalValueParameter<DistanceMatrix>("DistanceMatrix", "The matrix which contains the distances between the cities."));
114      Parameters.Add(new ValueParameter<BoolValue>("UseDistanceMatrix", "True if the coordinates based evaluators should calculate the distance matrix from the coordinates and use it for evaluation similar to the distance matrix evaluator, otherwise false.", new BoolValue(true)));
115      Parameters.Add(new OptionalValueParameter<Permutation>("BestKnownSolution", "The best known solution of this TSP instance."));
116
117      Maximization.Value = false;
118      MaximizationParameter.Hidden = true;
119      UseDistanceMatrixParameter.Hidden = true;
120      DistanceMatrixParameter.ReactOnValueToStringChangedAndValueItemImageChanged = false;
121
122      Coordinates = new DoubleMatrix(new double[,] {
123        { 100, 100 }, { 100, 200 }, { 100, 300 }, { 100, 400 },
124        { 200, 100 }, { 200, 200 }, { 200, 300 }, { 200, 400 },
125        { 300, 100 }, { 300, 200 }, { 300, 300 }, { 300, 400 },
126        { 400, 100 }, { 400, 200 }, { 400, 300 }, { 400, 400 }
127      });
128
129      SolutionCreator.PermutationParameter.ActualName = "TSPTour";
130      Evaluator.QualityParameter.ActualName = "TSPTourLength";
131      ParameterizeSolutionCreator();
132      ParameterizeEvaluator();
133
134      InitializeOperators();
135      RegisterEventHandlers();
136    }
137
138    #region Events
139    protected override void OnSolutionCreatorChanged() {
140      base.OnSolutionCreatorChanged();
141      SolutionCreator.PermutationParameter.ActualNameChanged += new EventHandler(SolutionCreator_PermutationParameter_ActualNameChanged);
142      ParameterizeSolutionCreator();
143      ParameterizeEvaluator();
144      ParameterizeAnalyzers();
145      ParameterizeOperators();
146    }
147    protected override void OnEvaluatorChanged() {
148      base.OnEvaluatorChanged();
149      Evaluator.QualityParameter.ActualNameChanged += new EventHandler(Evaluator_QualityParameter_ActualNameChanged);
150      ParameterizeEvaluator();
151      ParameterizeSolutionCreator();
152      UpdateMoveEvaluators();
153      ParameterizeAnalyzers();
154      if (Evaluator is ITSPCoordinatesPathEvaluator && Coordinates != null)
155        ClearDistanceMatrix();
156    }
157    private void CoordinatesParameter_ValueChanged(object sender, EventArgs e) {
158      if (Coordinates != null) {
159        Coordinates.ItemChanged += new EventHandler<EventArgs<int, int>>(Coordinates_ItemChanged);
160        Coordinates.Reset += new EventHandler(Coordinates_Reset);
161      }
162      if (Evaluator is ITSPCoordinatesPathEvaluator) {
163        ParameterizeSolutionCreator();
164        ClearDistanceMatrix();
165      }
166    }
167    private void Coordinates_ItemChanged(object sender, EventArgs<int, int> e) {
168      if (Evaluator is ITSPCoordinatesPathEvaluator) {
169        ClearDistanceMatrix();
170      }
171    }
172    private void Coordinates_Reset(object sender, EventArgs e) {
173      if (Evaluator is ITSPCoordinatesPathEvaluator) {
174        ParameterizeSolutionCreator();
175        ClearDistanceMatrix();
176      }
177    }
178    private void SolutionCreator_PermutationParameter_ActualNameChanged(object sender, EventArgs e) {
179      ParameterizeEvaluator();
180      ParameterizeAnalyzers();
181      ParameterizeOperators();
182    }
183    private void Evaluator_QualityParameter_ActualNameChanged(object sender, EventArgs e) {
184      ParameterizeAnalyzers();
185    }
186    #endregion
187
188    #region Helpers
189    [StorableHook(HookType.AfterDeserialization)]
190    private void AfterDeserialization() {
191      // BackwardsCompatibility3.3
192      #region Backwards compatible code (remove with 3.4)
193      OptionalValueParameter<DoubleMatrix> oldDistanceMatrixParameter = Parameters["DistanceMatrix"] as OptionalValueParameter<DoubleMatrix>;
194      if (oldDistanceMatrixParameter != null) {
195        Parameters.Remove(oldDistanceMatrixParameter);
196        Parameters.Add(new OptionalValueParameter<DistanceMatrix>("DistanceMatrix", "The matrix which contains the distances between the cities."));
197        DistanceMatrixParameter.GetsCollected = oldDistanceMatrixParameter.GetsCollected;
198        DistanceMatrixParameter.ReactOnValueToStringChangedAndValueItemImageChanged = false;
199        if (oldDistanceMatrixParameter.Value != null) {
200          DoubleMatrix oldDM = oldDistanceMatrixParameter.Value;
201          DistanceMatrix newDM = new DistanceMatrix(oldDM.Rows, oldDM.Columns, oldDM.ColumnNames, oldDM.RowNames);
202          newDM.SortableView = oldDM.SortableView;
203          for (int i = 0; i < newDM.Rows; i++)
204            for (int j = 0; j < newDM.Columns; j++)
205              newDM[i, j] = oldDM[i, j];
206          DistanceMatrixParameter.Value = (DistanceMatrix)newDM.AsReadOnly();
207        }
208      }
209
210      ValueParameter<DoubleMatrix> oldCoordinates = (Parameters["Coordinates"] as ValueParameter<DoubleMatrix>);
211      if (oldCoordinates != null) {
212        Parameters.Remove(oldCoordinates);
213        Parameters.Add(new OptionalValueParameter<DoubleMatrix>("Coordinates", "The x- and y-Coordinates of the cities.", oldCoordinates.Value, oldCoordinates.GetsCollected));
214      }
215
216      if (Operators.Count == 0) InitializeOperators();
217      #endregion
218      RegisterEventHandlers();
219    }
220
221    private void RegisterEventHandlers() {
222      CoordinatesParameter.ValueChanged += new EventHandler(CoordinatesParameter_ValueChanged);
223      if (Coordinates != null) {
224        Coordinates.ItemChanged += new EventHandler<EventArgs<int, int>>(Coordinates_ItemChanged);
225        Coordinates.Reset += new EventHandler(Coordinates_Reset);
226      }
227      SolutionCreator.PermutationParameter.ActualNameChanged += new EventHandler(SolutionCreator_PermutationParameter_ActualNameChanged);
228      Evaluator.QualityParameter.ActualNameChanged += new EventHandler(Evaluator_QualityParameter_ActualNameChanged);
229    }
230
231    private void InitializeOperators() {
232      Operators.Add(new BestTSPSolutionAnalyzer());
233      Operators.Add(new TSPAlleleFrequencyAnalyzer());
234      Operators.Add(new TSPPopulationDiversityAnalyzer());
235      ParameterizeAnalyzers();
236      var operators = new HashSet<IPermutationOperator>(new IPermutationOperator[] {
237        new OrderCrossover2(),
238        new InversionManipulator(),
239        new StochasticInversionMultiMoveGenerator()
240      }, new TypeEqualityComparer<IPermutationOperator>());
241      foreach (var op in ApplicationManager.Manager.GetInstances<IPermutationOperator>())
242        operators.Add(op);
243      Operators.AddRange(operators);
244      ParameterizeOperators();
245      UpdateMoveEvaluators();
246    }
247    private void UpdateMoveEvaluators() {
248      Operators.RemoveAll(x => x is ISingleObjectiveMoveEvaluator);
249      foreach (ITSPPathMoveEvaluator op in ApplicationManager.Manager.GetInstances<ITSPPathMoveEvaluator>())
250        if (op.EvaluatorType == Evaluator.GetType()) {
251          Operators.Add(op);
252        }
253      ParameterizeOperators();
254      OnOperatorsChanged();
255    }
256    private void ParameterizeSolutionCreator() {
257      if (Evaluator is ITSPDistanceMatrixEvaluator && DistanceMatrix != null)
258        SolutionCreator.LengthParameter.Value = new IntValue(DistanceMatrix.Rows);
259      else if (Evaluator is ITSPCoordinatesPathEvaluator && Coordinates != null)
260        SolutionCreator.LengthParameter.Value = new IntValue(Coordinates.Rows);
261      else {
262        SolutionCreator.LengthParameter.Value = null;
263        string error = "The given problem does not support the selected evaluator.";
264        if (Evaluator is ITSPDistanceMatrixEvaluator)
265          error += Environment.NewLine + "Please review that the " + DistanceMatrixParameter.Name + " parameter is defined or choose another evaluator.";
266        else error += Environment.NewLine + "Please review that the " + CoordinatesParameter.Name + " parameter is defined or choose another evaluator.";
267        PluginInfrastructure.ErrorHandling.ShowErrorDialog(error, null);
268      }
269      SolutionCreator.LengthParameter.Hidden = SolutionCreator.LengthParameter.Value != null;
270      SolutionCreator.PermutationTypeParameter.Value = new PermutationType(PermutationTypes.RelativeUndirected);
271      SolutionCreator.PermutationTypeParameter.Hidden = true;
272    }
273    private void ParameterizeEvaluator() {
274      if (Evaluator is ITSPPathEvaluator) {
275        ITSPPathEvaluator evaluator = (ITSPPathEvaluator)Evaluator;
276        evaluator.PermutationParameter.ActualName = SolutionCreator.PermutationParameter.ActualName;
277        evaluator.PermutationParameter.Hidden = true;
278      }
279      if (Evaluator is ITSPCoordinatesPathEvaluator) {
280        ITSPCoordinatesPathEvaluator evaluator = (ITSPCoordinatesPathEvaluator)Evaluator;
281        evaluator.CoordinatesParameter.ActualName = CoordinatesParameter.Name;
282        evaluator.CoordinatesParameter.Hidden = true;
283        evaluator.DistanceMatrixParameter.ActualName = DistanceMatrixParameter.Name;
284        evaluator.DistanceMatrixParameter.Hidden = true;
285        evaluator.UseDistanceMatrixParameter.ActualName = UseDistanceMatrixParameter.Name;
286        evaluator.UseDistanceMatrixParameter.Hidden = true;
287      }
288      if (Evaluator is ITSPDistanceMatrixEvaluator) {
289        var evaluator = (ITSPDistanceMatrixEvaluator)Evaluator;
290        evaluator.DistanceMatrixParameter.ActualName = DistanceMatrixParameter.Name;
291        evaluator.DistanceMatrixParameter.Hidden = true;
292      }
293    }
294    private void ParameterizeAnalyzers() {
295      if (BestTSPSolutionAnalyzer != null) {
296        BestTSPSolutionAnalyzer.QualityParameter.ActualName = Evaluator.QualityParameter.ActualName;
297        BestTSPSolutionAnalyzer.CoordinatesParameter.ActualName = CoordinatesParameter.Name;
298        BestTSPSolutionAnalyzer.PermutationParameter.ActualName = SolutionCreator.PermutationParameter.ActualName;
299        BestTSPSolutionAnalyzer.ResultsParameter.ActualName = "Results";
300        BestTSPSolutionAnalyzer.BestKnownQualityParameter.ActualName = BestKnownQualityParameter.Name;
301        BestTSPSolutionAnalyzer.BestKnownSolutionParameter.ActualName = BestKnownSolutionParameter.Name;
302        BestTSPSolutionAnalyzer.MaximizationParameter.ActualName = MaximizationParameter.Name;
303      }
304
305      if (TSPAlleleFrequencyAnalyzer != null) {
306        TSPAlleleFrequencyAnalyzer.MaximizationParameter.ActualName = MaximizationParameter.Name;
307        TSPAlleleFrequencyAnalyzer.CoordinatesParameter.ActualName = CoordinatesParameter.Name;
308        TSPAlleleFrequencyAnalyzer.DistanceMatrixParameter.ActualName = DistanceMatrixParameter.Name;
309        TSPAlleleFrequencyAnalyzer.SolutionParameter.ActualName = SolutionCreator.PermutationParameter.ActualName;
310        TSPAlleleFrequencyAnalyzer.QualityParameter.ActualName = Evaluator.QualityParameter.ActualName;
311        TSPAlleleFrequencyAnalyzer.BestKnownSolutionParameter.ActualName = BestKnownSolutionParameter.Name;
312        TSPAlleleFrequencyAnalyzer.ResultsParameter.ActualName = "Results";
313      }
314
315      if (TSPPopulationDiversityAnalyzer != null) {
316        TSPPopulationDiversityAnalyzer.MaximizationParameter.ActualName = MaximizationParameter.Name;
317        TSPPopulationDiversityAnalyzer.SolutionParameter.ActualName = SolutionCreator.PermutationParameter.ActualName;
318        TSPPopulationDiversityAnalyzer.QualityParameter.ActualName = Evaluator.QualityParameter.ActualName;
319        TSPPopulationDiversityAnalyzer.ResultsParameter.ActualName = "Results";
320      }
321    }
322    private void ParameterizeOperators() {
323      foreach (IPermutationCrossover op in Operators.OfType<IPermutationCrossover>()) {
324        op.ParentsParameter.ActualName = SolutionCreator.PermutationParameter.ActualName;
325        op.ParentsParameter.Hidden = true;
326        op.ChildParameter.ActualName = SolutionCreator.PermutationParameter.ActualName;
327        op.ChildParameter.Hidden = true;
328      }
329      foreach (IPermutationManipulator op in Operators.OfType<IPermutationManipulator>()) {
330        op.PermutationParameter.ActualName = SolutionCreator.PermutationParameter.ActualName;
331        op.PermutationParameter.Hidden = true;
332      }
333      foreach (IPermutationMoveOperator op in Operators.OfType<IPermutationMoveOperator>()) {
334        op.PermutationParameter.ActualName = SolutionCreator.PermutationParameter.ActualName;
335        op.PermutationParameter.Hidden = true;
336      }
337      foreach (ITSPPathMoveEvaluator op in Operators.OfType<ITSPPathMoveEvaluator>()) {
338        op.CoordinatesParameter.ActualName = CoordinatesParameter.Name;
339        op.CoordinatesParameter.Hidden = true;
340        op.DistanceMatrixParameter.ActualName = DistanceMatrixParameter.Name;
341        op.DistanceMatrixParameter.Hidden = true;
342        op.UseDistanceMatrixParameter.ActualName = UseDistanceMatrixParameter.Name;
343        op.UseDistanceMatrixParameter.Hidden = true;
344        op.QualityParameter.ActualName = Evaluator.QualityParameter.ActualName;
345        op.QualityParameter.Hidden = true;
346        op.PermutationParameter.ActualName = SolutionCreator.PermutationParameter.ActualName;
347        op.PermutationParameter.Hidden = true;
348      }
349      foreach (IPermutationMultiNeighborhoodShakingOperator op in Operators.OfType<IPermutationMultiNeighborhoodShakingOperator>()) {
350        op.PermutationParameter.ActualName = SolutionCreator.PermutationParameter.ActualName;
351        op.PermutationParameter.Hidden = true;
352      }
353    }
354
355    private void ClearDistanceMatrix() {
356      DistanceMatrixParameter.Value = null;
357    }
358    #endregion
359
360    public void Load(TSPData data) {
361      if (data.Coordinates == null && data.Distances == null)
362        throw new System.IO.InvalidDataException("The given instance specifies neither coordinates nor distances!");
363      if (data.Dimension > DistanceMatrixSizeLimit && (data.DistanceMeasure == DistanceMeasure.Att
364        || data.DistanceMeasure == DistanceMeasure.Manhattan
365        || data.DistanceMeasure == DistanceMeasure.Maximum
366        || data.DistanceMeasure == DistanceMeasure.UpperEuclidean))
367        throw new System.IO.InvalidDataException("The given instance uses an unsupported distance measure and is too large for using a distance matrix.");
368      if (data.Coordinates != null && data.Coordinates.GetLength(1) != 2)
369        throw new System.IO.InvalidDataException("The coordinates of the given instance are not in the right format, there need to be one row for each customer and two columns for the x and y coordinates.");
370
371      Name = data.Name;
372      Description = data.Description;
373
374      bool clearCoordinates = false, clearDistanceMatrix = false;
375      if (data.Coordinates != null && data.Coordinates.GetLength(0) > 0)
376        Coordinates = new DoubleMatrix(data.Coordinates);
377      else clearCoordinates = true;
378
379      TSPEvaluator evaluator;
380      if (data.DistanceMeasure == DistanceMeasure.Att
381        || data.DistanceMeasure == DistanceMeasure.Manhattan
382        || data.DistanceMeasure == DistanceMeasure.Maximum
383        || data.DistanceMeasure == DistanceMeasure.UpperEuclidean) {
384        evaluator = new TSPDistanceMatrixEvaluator();
385        UseDistanceMatrix = new BoolValue(true);
386        DistanceMatrix = new DistanceMatrix(data.GetDistanceMatrix());
387      } else if (data.DistanceMeasure == DistanceMeasure.Direct && data.Distances != null) {
388        evaluator = new TSPDistanceMatrixEvaluator();
389        UseDistanceMatrix = new BoolValue(true);
390        DistanceMatrix = new DistanceMatrix(data.Distances);
391      } else {
392        clearDistanceMatrix = true;
393        UseDistanceMatrix = new BoolValue(data.Dimension <= DistanceMatrixSizeLimit);
394        switch (data.DistanceMeasure) {
395          case DistanceMeasure.Euclidean:
396            evaluator = new TSPEuclideanPathEvaluator();
397            break;
398          case DistanceMeasure.RoundedEuclidean:
399            evaluator = new TSPRoundedEuclideanPathEvaluator();
400            break;
401          case DistanceMeasure.Geo:
402            evaluator = new TSPGeoPathEvaluator();
403            break;
404          default:
405            throw new InvalidDataException("An unknown distance measure is given in the instance!");
406        }
407      }
408      evaluator.QualityParameter.ActualName = "TSPTourLength";
409      Evaluator = evaluator;
410
411      // reset them after assigning the evaluator
412      if (clearCoordinates) Coordinates = null;
413      if (clearDistanceMatrix) DistanceMatrix = null;
414
415      BestKnownSolution = null;
416      BestKnownQuality = null;
417
418      if (data.BestKnownTour != null) {
419        try {
420          EvaluateAndLoadTour(data.BestKnownTour);
421        } catch (InvalidOperationException) {
422          if (data.BestKnownQuality.HasValue)
423            BestKnownQuality = new DoubleValue(data.BestKnownQuality.Value);
424        }
425      } else if (data.BestKnownQuality.HasValue) {
426        BestKnownQuality = new DoubleValue(data.BestKnownQuality.Value);
427      }
428      OnReset();
429    }
430
431    public void EvaluateAndLoadTour(int[] tour) {
432      var route = new Permutation(PermutationTypes.RelativeUndirected, tour);
433      BestKnownSolution = route;
434
435      double quality;
436      if (Evaluator is ITSPDistanceMatrixEvaluator) {
437        quality = TSPDistanceMatrixEvaluator.Apply(DistanceMatrix, route);
438      } else if (Evaluator is ITSPCoordinatesPathEvaluator) {
439        quality = TSPCoordinatesPathEvaluator.Apply((TSPCoordinatesPathEvaluator)Evaluator, Coordinates, route);
440      } else {
441        throw new InvalidOperationException("Cannot calculate solution quality, evaluator type is unknown.");
442      }
443      BestKnownQuality = new DoubleValue(quality);
444    }
445  }
446}
Note: See TracBrowser for help on using the repository browser.