Free cookie consent management tool by TermsFeed Policy Generator

source: trunk/sources/HeuristicLab.Services.OKB/3.3/RunnerService.cs @ 4279

Last change on this file since 4279 was 4279, checked in by swagner, 14 years ago

Integrated OKB services (#1166)

File size: 15.8 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.Collections.Generic;
24using System.Data.Linq;
25using System.Linq;
26using System.ServiceModel;
27using HeuristicLab.Services.OKB.DataAccess;
28using log4net;
29
30namespace HeuristicLab.Services.OKB {
31
32  /// <summary>
33  /// Implementation of the <see cref="IRunnerService"/>.
34  /// </summary>
35  [ServiceBehavior(
36    InstanceContextMode = InstanceContextMode.PerSession)]
37  public class RunnerService : IRunnerService, IDisposable {
38
39    private Guid sessionID;
40    private static ILog logger = LogManager.GetLogger(typeof(RunnerService));
41
42    private void Log(string message, params object[] args) {
43      using (log4net.ThreadContext.Stacks["NDC"].Push(sessionID.ToString())) {
44        logger.Info(String.Format(message, args));
45      }
46    }
47
48    /// <summary>
49    /// Initializes a new instance of the <see cref="RunnerService"/> class.
50    /// </summary>
51    public RunnerService() {
52      sessionID = Guid.NewGuid();
53      Log("Instantiating new service");
54    }
55
56    /// <summary>
57    /// Obtain a "starter kit" of information, containing:
58    /// <list>
59    /// <item>algorithm classes including algorithms</item>
60    /// <item>problem classes including problems</item>
61    /// <item>projects</item>
62    /// </list>
63    /// </summary>
64    public StarterKit GetStarterKit(string platformName) {
65      Log("distributing starter kit");
66      Platform platform;
67      using (OKBDataContext okb = new OKBDataContext()) {
68        try {
69          platform = okb.Platforms.Single(p => p.Name == platformName);
70        }
71        catch (InvalidOperationException) {
72          throw new FaultException(String.Format(
73            "Invalid platform name \"{0}\" available platforms are: \"{1}\"",
74            platformName,
75            string.Join("\", \"", okb.Platforms.Select(p => p.Name).ToArray())));
76        }
77      }
78      using (OKBDataContext okb = new OKBDataContext()) {
79        DataLoadOptions dlo = new DataLoadOptions();
80        dlo.LoadWith<AlgorithmClass>(ac => ac.Algorithms);
81        dlo.AssociateWith<AlgorithmClass>(ac => ac.Algorithms.Where(a => a.Platform == platform));
82        dlo.LoadWith<ProblemClass>(p => p.Problems);
83        dlo.AssociateWith<ProblemClass>(pc => pc.Problems.Where(p => p.Platform == platform));
84        okb.LoadOptions = dlo;
85        var StarterKit = new StarterKit() {
86          AlgorithmClasses = okb.AlgorithmClasses.ToList(),
87          ProblemClasses = okb.ProblemClasses.ToList(),
88          Projects = okb.Projects.ToList()
89        };
90        return StarterKit;
91      }
92    }
93
94    /// <summary>
95    /// Augument the given algorithm and problem entities with information
96    /// to conduct an experiment. This will add algorithm parameters and results and
97    /// problem characteristic values and solution representation as well as data
98    /// necessary for deserialization.
99    /// </summary>
100    public ExperimentKit PrepareExperiment(Algorithm algorithm, Problem problem) {
101      Log("preparing experiment: {0}@{1}", algorithm.Name, problem.Name);
102      OKBDataContext okb = new OKBDataContext();
103      try {
104        DataLoadOptions dlo = new DataLoadOptions();
105        dlo.LoadWith<Problem>(p => p.SolutionRepresentation);
106        dlo.LoadWith<Parameter>(p => p.DataType);
107        dlo.LoadWith<Result>(r => r.DataType);
108        dlo.LoadWith<Algorithm_Parameter>(ap => ap.Parameter);
109        dlo.LoadWith<Problem_Parameter>(pp => pp.Parameter);
110        dlo.LoadWith<Algorithm_Result>(ar => ar.Result);
111        okb.LoadOptions = dlo;
112        algorithm = okb.Algorithms.Single(a => a.Id == algorithm.Id);
113        problem = okb.Problems.Single(p => p.Id == problem.Id);
114        algorithm.Algorithm_Parameters.Load();
115        algorithm.Algorithm_Results.Load();
116        problem.IntProblemCharacteristicValues.Load();
117        problem.FloatProblemCharacteristicValues.Load();
118        problem.CharProblemCharacteristicValues.Load();
119        problem.Problem_Parameters.Load();
120        return new ExperimentKit() {
121          Algorithm = algorithm,
122          Problem = problem
123        };
124      }
125      catch (Exception x) {
126        Log("exception caught: " + x.ToString());
127        throw new FaultException("Excaption caught: " + x.ToString());
128      }
129    }
130
131    /// <summary>
132    /// Adds the a new <see cref="Experiment"/>
133    ///   <see cref="Run"/>.
134    /// The <see cref="Experiment"/> is created if necessary as well as
135    /// all <see cref="Parameter"/>s and <see cref="Result"/>s. If an
136    /// identical experiment has been conducted before the new run is
137    /// linked to this previous experiment instead.
138    /// </summary>
139    /// <param name="algorithm">The algorithm.</param>
140    /// <param name="problem">The problem.</param>
141    /// <param name="project">The project.</param>
142    public void AddRun(Algorithm algorithm, Problem problem, Project project) {
143      Log("adding run for {0}@{1}({2})[{3}, {4}]",
144        algorithm.Name, problem.Name, project.Name, authentication.User.Name, authentication.Client.Name);
145      try {
146        using (OKBDataContext okb = new OKBDataContext()) {
147          var user = okb.Users.Single(u => u.Id == authentication.User.Id);
148          var client = okb.Clients.Single(c => c.Id == authentication.Client.Id);
149          Experiment experiment = GetOrCreateExperiment(algorithm, problem, project, authentication.User, okb);
150          Run run = new Run() {
151            Experiment = experiment,
152            UserId = authentication.User.Id,
153            ClientId = authentication.Client.Id,
154            FinishedDate = DateTime.Now,
155            ResultValues = algorithm.ResultValues
156          };
157          okb.Runs.InsertOnSubmit(run);
158          okb.SubmitChanges();
159        }
160      }
161      catch (Exception x) {
162        Log(x.ToString());
163        throw new FaultException("Could not add run: " + x.ToString());
164      }
165    }
166
167    private Experiment GetOrCreateExperiment(Algorithm algorithm, Problem problem,
168        Project project, User user, OKBDataContext okb) {
169      MatchResults(algorithm.Results, okb);
170      EnsureParametersExist(algorithm.Parameters, okb);
171      var experimentQuery = CreateExperimentQuery(algorithm, problem, project, okb);
172      if (experimentQuery.Count() > 0) {
173        if (experimentQuery.Count() > 1)
174          Log("Warning: duplicate experiment found");
175        Log("reusing existing experiment");
176        Experiment experiment = experimentQuery.First();
177        if (experiment.ExperimentCreators.Where(ec => ec.UserId == user.Id).Count() == 0) {
178          experiment.ExperimentCreators.Add(new ExperimentCreator() { UserId = user.Id });
179        }
180        return experiment;
181      } else {
182        Log("creating new experiment");
183        Experiment experiment = new Experiment() {
184          AlgorithmId = algorithm.Id,
185          ProblemId = problem.Id,
186          ProjectId = project.Id,
187          ParameterValues = algorithm.ParameterValues
188        };
189        experiment.ExperimentCreators.Add(new ExperimentCreator() { UserId = user.Id });
190        okb.Experiments.InsertOnSubmit(experiment);
191        return experiment;
192      }
193    }
194
195    private void MatchResults(IQueryable<Result> results, OKBDataContext okb) {
196      foreach (var result in results.Where(r => r.Name != null)) {
197        result.Id = okb.Results.Single(r => r.Name == result.Name).Id;
198        Log("mapping named result {0} -> Id {1}", result.Name, result.Id);
199        var value = result.ResultValue;
200        if (value != null) {
201          result.ResultValue = (IResultValue)Activator.CreateInstance(value.GetType());
202          result.ResultValue.Result = result;
203          result.ResultValue.Value = value.Value;
204        }
205      }
206    }
207
208    private void EnsureParametersExist(IQueryable<Parameter> parameters, OKBDataContext okb) {
209      foreach (var param in parameters.Where(p => p.DataType != null && p.DataType.ClrName != null)) {
210        DataType dataType = GetOrCreateDataType(okb, param.DataType.ClrName);
211        param.DataType = new DataType() { Id = dataType.Id };
212        Log("mapping datatype {0} to id {1}", dataType.ClrName, dataType.Id);
213      }
214      okb.SubmitChanges();
215      var namedParams = parameters.Where(p => p.Name != null);
216      var newParams = namedParams.Except(okb.Parameters, new NameComprarer());
217      foreach (var p in newParams) {
218        Log("creating new parameter {0} ({2}:{1})", p.Name, p.DataType.ClrName, p.DataType.Id);
219        okb.Parameters.InsertOnSubmit(new Parameter() {
220          Name = p.Name,
221          DataTypeId = p.DataTypeId,
222        });
223      }
224      okb.SubmitChanges();
225      foreach (var np in namedParams) {
226        np.Id = okb.Parameters.Single(p => p.Name == np.Name).Id;
227        Log("mapping named parameter {0} -> Id {1}", np.Name, np.Id);
228        var value = np.ParameterValue;
229        if (value != null) {
230          OperatorParameterValue opVal = value as OperatorParameterValue;
231          np.ParameterValue = (IParameterValue)Activator.CreateInstance(value.GetType());
232          np.ParameterValue.Parameter = np;
233          np.ParameterValue.Value = value.Value;
234          if (opVal != null) {
235            OperatorParameterValue newVal = np.ParameterValue as OperatorParameterValue;
236            DataType dataType = GetOrCreateDataType(okb, opVal.DataType.ClrName);
237            newVal.DataType = new DataType() { Id = dataType.Id };
238            Log("mapping operator parameter datatype {0} to id {1}", dataType.ClrName, dataType.Id);
239          }
240        }
241      }
242    }
243
244    private DataType GetOrCreateDataType(OKBDataContext okb, string clrName) {
245      DataType dataType = okb.DataTypes.SingleOrDefault(dt => dt.ClrName == clrName);
246      if (dataType == null) {
247        Log("creating new datatype for ", clrName);
248        dataType = new DataType() {
249          ClrName = clrName,
250          SqlName = "BLOB",
251        };
252        okb.DataTypes.InsertOnSubmit(dataType);
253        okb.SubmitChanges();
254      }
255      return dataType;
256    }
257
258    private IQueryable<Experiment> CreateExperimentQuery(Algorithm algorithm, Problem problem, Project project,
259        OKBDataContext okb) {
260      var experimentQuery =
261        from x in okb.Experiments
262        where x.Algorithm == algorithm
263        where x.Problem == problem
264        where x.ProjectId == project.Id
265        select x;
266      foreach (IntParameterValue ipv in algorithm.IntParameterValues) {
267        experimentQuery = experimentQuery
268          .Where(x => x.IntParameterValues.Any(p =>
269            p.ParameterId == ipv.ParameterId && p.Value == ipv.Value));
270      }
271      foreach (FloatParameterValue fpv in algorithm.FloatParameterValues) {
272        experimentQuery = experimentQuery
273          .Where(x => x.FloatParameterValues.Any(p =>
274            p.ParameterId == fpv.ParameterId && p.Value == fpv.Value));
275      }
276      foreach (CharParameterValue cpv in algorithm.CharParameterValues) {
277        experimentQuery = experimentQuery
278          .Where(x => x.CharParameterValues.Any(p =>
279            p.ParameterId == cpv.ParameterId && p.Value == cpv.Value));
280      }
281      foreach (OperatorParameterValue opv in algorithm.OperatorParameterValues) {
282        experimentQuery = experimentQuery
283          .Where(x => x.OperatorParameterValues.Any(p =>
284            p.ParameterId == opv.ParameterId && p.DataTypeId == opv.DataTypeId));
285      }
286      Log("experiment query: ", experimentQuery.Expression.ToString());
287      return experimentQuery;
288    }
289
290    /// <summary>
291    /// Determines whether this instance is connected.
292    /// </summary>
293    /// <returns>
294    ///   <c>true</c> if this instance is connected; otherwise, <c>false</c>.
295    /// </returns>
296    public bool IsConnected() {
297      return authentication != null;
298    }
299
300    Authentication authentication = null;
301
302    /// <summary>
303    /// Logs the specified username in. In case the user or client
304    /// does not exist yet, they are created on the server. This
305    /// method is currently not used for authentication but merely
306    /// for auditing.
307    /// </summary>
308    /// <param name="username">The username.</param>
309    /// <param name="clientname">The clientname.</param>
310    /// <returns>
311    ///   <c>true</c> if the login was successful; <c>false</c> otherwise.
312    /// </returns>
313    public bool Login(string username, string clientname) {
314      Log("Authenticating {0}@{1}", username, clientname);
315      if (string.IsNullOrEmpty(username) ||
316          string.IsNullOrEmpty(clientname) ||
317          ServiceSecurityContext.Current.IsAnonymous) {
318        Log("rejecting anonymous login");
319        return false;
320      }
321      using (OKBDataContext okb = new OKBDataContext()) {
322        authentication = new Authentication() {
323          User = okb.Users.SingleOrDefault(u => u.Name == username),
324          Client = okb.Clients.SingleOrDefault(c => c.Name == clientname)
325        };
326        if (authentication.User == null) {
327          User user = new User() { Name = username, Id = Guid.NewGuid() };
328          okb.Users.InsertOnSubmit(user);
329          authentication.User = user;
330          okb.SubmitChanges();
331        }
332        if (authentication.Client == null) {
333          Client client = new Client() { Name = clientname, Id = Guid.NewGuid() };
334          okb.Clients.InsertOnSubmit(client);
335          authentication.Client = client;
336          okb.SubmitChanges();
337        }
338        Log("  auth = {0}", authentication);
339        return true;
340      }
341    }
342
343    /// <summary>
344    /// Logout out and closes the connection.
345    /// </summary>
346    public void Logout() {
347      Log("Logging out");
348      authentication = null;
349    }
350
351    /// <summary>
352    /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
353    /// </summary>
354    public void Dispose() {
355      Log("Closing session...");
356      Logout();
357    }
358  }
359
360  /// <summary>
361  /// Compares two parameters by name.
362  /// </summary>
363  internal class NameComprarer : IEqualityComparer<Parameter> {
364
365    /// <summary>
366    /// Determines whether the specified objects are equal.
367    /// </summary>
368    /// <param name="x">The first object of type <paramref name="T"/> to compare.</param>
369    /// <param name="y">The second object of type <paramref name="T"/> to compare.</param>
370    /// <returns>
371    /// true if the specified objects are equal; otherwise, false.
372    /// </returns>
373    public bool Equals(Parameter x, Parameter y) {
374      return x.Name == y.Name;
375    }
376
377    /// <summary>
378    /// Returns a hash code for this instance.
379    /// </summary>
380    /// <param name="obj">The obj.</param>
381    /// <returns>
382    /// A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table.
383    /// </returns>
384    /// <exception cref="T:System.ArgumentNullException">
385    /// The type of <paramref name="obj"/> is a reference type and <paramref name="obj"/> is null.
386    /// </exception>
387    public int GetHashCode(Parameter obj) {
388      return obj.Name.GetHashCode();
389    }
390
391  }
392}
Note: See TracBrowser for help on using the repository browser.