source: branches/3073_IA_constraint_splitting/HeuristicLab.Problems.DataAnalysis/3.4/Implementation/Interval/IntervalConstraintsParser.cs @ 17768

Last change on this file since 17768 was 17768, checked in by chaider, 14 months ago

#3073

  • Removed Region class and used IntervallCollection instead
  • Changed Parser to work with IntervalColletions
  • Moved CheckConstraint methods from Analyzer to IntervalUtil class
  • Added CheckConstraint method to interface to check if an interval is in a given constraint
  • Added possibility to stop splitting as soon as a constraint is fulfiled
File size: 12.4 KB
Line 
1#region License Information
2
3/* HeuristicLab
4 * Copyright (C) 2002-2019 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
5 *
6 * This file is part of HeuristicLab.
7 *
8 * HeuristicLab is free software: you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation, either version 3 of the License, or
11 * (at your option) any later version.
12 *
13 * HeuristicLab is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
20 */
21
22#endregion
23
24using System;
25using System.Collections.Generic;
26using System.Globalization;
27using System.Linq;
28using System.Text.RegularExpressions;
29
30namespace HeuristicLab.Problems.DataAnalysis {
31  public static class IntervalConstraintsParser {
32    public static IEnumerable<IntervalConstraint> ParseInput(string              inputText, string target,
33                                                             IEnumerable<string> variables) {
34      if (string.IsNullOrEmpty(inputText)) throw new ArgumentNullException("No input text has been provided.");
35      if (string.IsNullOrEmpty(target)) throw new ArgumentNullException("No target variable has been provided.");
36      if (variables == null) throw new ArgumentNullException("No variables have been provided.");
37      if (!variables.Any()) throw new ArgumentException("Variables are empty.");
38
39      var lines = inputText.Split(new[] {"\r\n", "\r", "\n"}, StringSplitOptions.None);
40      foreach (var line in lines) {
41        var trimmedLine = line.TrimStart();
42        //Check for target-variable constraint
43        if (trimmedLine.StartsWith("Target:")) {
44          var start            = "Target:".Length;
45          var end              = trimmedLine.Length;
46          var targetConstraint = trimmedLine.Substring(start, end - start);
47
48          var match =
49          Regex.Match(targetConstraint,
50                        @"(['](.*)[']|(.*[^\s]))" +
51                        @"\s*(\bin\b)" +
52                        @"\s*([\[])" +
53                        @"\s*(\S*)" + // 6: interval lower bound
54                        @"\s*(\.{2})" +
55                        @"\s*(\S*)" + // 8: interval upper bound
56                        @"\s*([\]])" +
57                        @"(" +
58                          @"\s*,\s*(\S*)\s*=\s*" + // 11: variable name
59                          @"([(])\s*" +
60                          @"(\S*)\s*" + // 13: region lower bound
61                          @"(\.{2})\s*" +
62                          @"(\S*)\s*" + // 15: region upper bound
63                          @"([)])" +
64                        @")*" +
65                        @"\s*(<(\S*)>)?"); // 17, 18
66
67         
68          if (match.Success) {
69            if (match.Groups.Count < 19) throw new ArgumentException("The target-constraint is not complete.", line);
70
71            var targetVariable = match.Groups[1].Value.Trim();
72            if (targetVariable.StartsWith("'") && targetVariable.EndsWith("'"))
73              targetVariable = targetVariable.Substring(1, targetVariable.Length - 2);
74
75            if (targetVariable != target)
76              throw new
77                ArgumentException($"The target variable {targetVariable}  does not match the provided target {target}.",
78                                  line);
79
80            var lowerBound         = ParseIntervalBounds(match.Groups[6].Value);
81            var upperBound         = ParseIntervalBounds(match.Groups[8].Value);
82            var expression         = "Target:" + match.Groups[0].Value;
83            var parsedTarget       = match.Groups[1].Value.Trim();
84            var variable           = targetVariable;
85            var isEnabled          = true;
86            var numberOfDerivation = 0;
87            var interval           = new Interval(lowerBound, upperBound);
88            var weight = 1.0;
89
90            if (match.Groups[18].Success && !string.IsNullOrWhiteSpace(match.Groups[18].Value))
91              weight = ParseWeight(match.Groups[18].Value);
92
93            if (match.Groups[10].Success)
94            {
95              IntervalCollection regions = new IntervalCollection();
96              // option variables found
97              for(int idx = 0; idx < match.Groups[10].Captures.Count; ++idx)
98              {
99                KeyValuePair<string, Interval> region = ParseRegion(
100                  match.Groups[11].Captures[idx].Value,
101                  match.Groups[13].Captures[idx].Value,
102                  match.Groups[15].Captures[idx].Value);
103                if (regions.GetReadonlyDictionary().All(r => r.Key != region.Key))
104                  regions.AddInterval(region.Key, region.Value);
105                else
106                  throw new ArgumentException("A constraint cannot contain multiple regions of the same variable.");
107              }
108              yield return new IntervalConstraint(expression, variable, parsedTarget, numberOfDerivation, interval, regions, weight, isEnabled);
109            }
110            else
111              yield return new IntervalConstraint(expression, variable, parsedTarget, numberOfDerivation, interval, weight, isEnabled);
112          }
113          else
114            throw new ArgumentException("The inserted target constraint is not valid.", line);
115
116          //Check for derivation
117        }
118        else if (trimmedLine.StartsWith("d") || trimmedLine.StartsWith("\u2202")) {
119          var match = Regex.Match(trimmedLine,
120                                    @"([d∂])" +
121                                    @"([²³]?)\s*" +
122                                    @"(['](.*)[']|(.*[^\s]))\s*" +
123                                    @"(\/)\s*" +
124                                    @"([d∂])\s*" +
125                                    @"(['](.*)[']|(.*[^\s²³]))\s*" +
126                                    @"([²³]?)\s*\bin\b\s*" +
127                                    @"([\[])\s*" +
128                                    @"(\S*)\s*" +
129                                    @"(\.{2})\s*" +
130                                    @"(\S*)\s*" +
131                                    @"([\]])" +
132                                    @"(" +
133                                      @"\s*,\s*(\S*)\s*=\s*" + // 18: variable name
134                                      @"([(])\s*" +
135                                      @"(\S*)\s*" + // 20: region lower bound
136                                      @"(\.{2})\s*" +
137                                      @"(\S*)\s*" + // 22: region upper bound
138                                      @"([)])" +
139                                    @")*" +
140                                    @"\s*(<(\S*)>)?"); // 24, 25
141
142          if (match.Success) {
143            if (match.Groups.Count < 26)
144              throw new ArgumentException("The given derivation-constraint is not complete.", line);
145
146            var derivationTarget   = match.Groups[3].Value.Trim();
147            var derivationVariable = match.Groups[8].Value.Trim();
148
149            if (match.Groups[3].Value.Trim().StartsWith("'") && match.Groups[3].Value.Trim().EndsWith("'"))
150              derivationTarget = derivationTarget.Substring(1, derivationTarget.Length - 2);
151
152            if (match.Groups[8].Value.Trim().StartsWith("'") && match.Groups[8].Value.Trim().EndsWith("'"))
153              derivationVariable = derivationVariable.Substring(1, derivationVariable.Length - 2);
154
155            if (derivationTarget != target)
156              throw new
157                ArgumentException($"The target variable {derivationTarget}  does not match the provided target {target}.",
158                                  line);
159
160            if (variables.All(v => v != derivationVariable))
161              throw new ArgumentException($"The given variable {derivationVariable} does not exist in the dataset.",
162                                          line);
163
164            if (match.Groups[2].Value.Trim() != "" || match.Groups[11].Value.Trim() != "") {
165              if (match.Groups[2].Value.Trim() == "" || match.Groups[11].Value.Trim() == "")
166                throw new ArgumentException("Number of derivation has to be written on both sides.", line);
167              if (match.Groups[2].Value.Trim() != match.Groups[11].Value.Trim())
168                throw new ArgumentException("Derivation number is not equal on both sides.", line);
169            }
170
171            var lowerBound         = ParseIntervalBounds(match.Groups[13].Value);
172            var upperBound         = ParseIntervalBounds(match.Groups[15].Value);
173            var expression         = match.Groups[0].Value;
174            var parsedTarget       = derivationTarget;
175            var isEnabled          = true;
176            var variable           = derivationVariable;
177            var numberOfDerivation = ParseDerivationCount(match.Groups[2].Value.Trim());
178            var interval           = new Interval(lowerBound, upperBound);
179            var weight             = 1.0;
180
181            if(match.Groups[25].Success && !string.IsNullOrWhiteSpace(match.Groups[25].Value))
182              weight = ParseWeight(match.Groups[25].Value);
183
184            if(match.Groups[17].Success)
185            {
186              IntervalCollection regions = new IntervalCollection();
187              // option variables found
188              for (int idx = 0; idx < match.Groups[17].Captures.Count; ++idx)
189              {
190                KeyValuePair<string, Interval> region = ParseRegion(
191                  match.Groups[18].Captures[idx].Value,
192                  match.Groups[20].Captures[idx].Value,
193                  match.Groups[22].Captures[idx].Value);
194                if (regions.GetReadonlyDictionary().All(r => r.Key != region.Key))
195                  regions.AddInterval(region.Key, region.Value);
196                else
197                  throw new ArgumentException("A constraint cannot contain multiple regions of the same variable.");
198              }
199              yield return new IntervalConstraint(expression, variable, parsedTarget, numberOfDerivation, interval, regions, weight, isEnabled);
200            } else
201              yield return new IntervalConstraint(expression, variable, parsedTarget, numberOfDerivation, interval, weight, isEnabled);
202          }
203          else
204            throw new ArgumentException("The inserted derivation constraint is not valid.", line);
205
206          //Check for comment
207        }
208        else if (trimmedLine.StartsWith("#") || trimmedLine == "") {
209          //If it is a comment just continue without saving anything
210        }
211        else {
212          throw new
213            ArgumentException("Error at your constraints definition constraints have to start with (Target: | d | \u2202 | #)",
214                              line);
215        }
216      }
217    }
218
219    private static KeyValuePair<string, Interval> ParseRegion(string variable, string lb, string ub)
220    {
221      var regionLb = ParseIntervalBounds(lb);
222      var regionUb = ParseIntervalBounds(ub);
223      return new KeyValuePair<string, Interval>(variable, new Interval(regionLb, regionUb));
224      //return new Region(variable, new Interval(regionLb, regionUb));
225    }
226
227    private static double ParseIntervalBounds(string input) {
228      input = input.ToLower();
229      switch (input) {
230        case "+inf.":
231        case "inf.":
232          return double.PositiveInfinity;
233        case "-inf.":
234          return double.NegativeInfinity;
235        default: {
236          if (double.TryParse(input, NumberStyles.Any, CultureInfo.InvariantCulture, out var value))
237            return value;
238          throw new ArgumentException("The given boundary is not a double value!");
239        }
240      }
241    }
242
243    private static double ParseWeight(string input)
244    {
245      if (double.TryParse(input, NumberStyles.Any, CultureInfo.InvariantCulture, out var value))
246        return value;
247      throw new ArgumentException("The given weight is not a double value!");
248    }
249
250    private static int ParseDerivationCount(string input) {
251      switch (input) {
252        case "":
253          return 1;
254        case "²":
255          return 2;
256        case "³":
257          return 3;
258        default:
259          int.TryParse(input, out var value);
260          return value;
261      }
262    }
263  }
264}
Note: See TracBrowser for help on using the repository browser.