source: branches/2971_named_intervals/HeuristicLab.Problems.DataAnalysis/3.4/Implementation/Interval/IntervalConstraintsParser.cs @ 16957

Last change on this file since 16957 was 16957, checked in by chaider, 4 months ago

#2971 Fixed parsing of seperator between lower- and upper-bound to match exactly two points

File size: 7.7 KB
Line 
1#region License Information
2/* HeuristicLab
3 * Copyright (C) 2002-2019 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
21using System;
22using System.Collections.Generic;
23using System.Globalization;
24using System.Linq;
25using System.Text.RegularExpressions;
26
27namespace HeuristicLab.Problems.DataAnalysis {
28  public static class IntervalConstraintsParser {
29
30    public static IEnumerable<IntervalConstraint> ParseInput(string input, string target = "", IEnumerable<string> variables = null) {
31      var lines = input.Split(new[] { "\r\n", "\r", "\n" }, StringSplitOptions.None);
32      foreach (var line in lines) {
33        var trimmedLine = line.TrimStart();
34        //Check for target-variable constraint
35        if (trimmedLine.StartsWith("Target:")) {
36          var start = "Target:".Length;
37          var end = trimmedLine.Length;
38          var targetConstraint = trimmedLine.Substring(start, end-start);
39          var match = Regex.Match(targetConstraint,
40            @"(['](.*)[']|(.*[^\s]))\s*(\bin\b)\s*([\[\]])\s*(\S*)\s*(\.{2})\s*(\S*)\s*([\[\]])");
41          if (match.Success) {
42            if (match.Groups.Count != 10) {
43              throw new ArgumentException("The given target-constraint is not complete!");
44            } else {
45              var targetVariable = match.Groups[1].Value.Trim();
46              if (match.Groups[1].Value.Trim().StartsWith("'") && match.Groups[1].Value.Trim().EndsWith("'")) {
47                targetVariable = targetVariable.Substring(1, targetVariable.Length - 2);
48              }
49              if (target != "") {
50                if (targetVariable != target) {
51                  throw new ArgumentException("The given target variable is not in the given dataset!");
52                }
53              }
54              var lowerBound = ParseIntervalBounds(match.Groups[6].Value);
55              var upperBound = ParseIntervalBounds(match.Groups[8].Value);
56              var expression = "Target:" + match.Groups[0].Value;
57              var parsedTarget = match.Groups[1].Value.Trim();
58              var variable = targetVariable;
59              var inclLowerBound = match.Groups[5].Value.Trim() == "[";
60              var inclUpperBound = match.Groups[9].Value.Trim() == "]";
61              var isEnabled = true;
62              var numberOfDerivation = 0;
63              var interval = new Interval(lowerBound, upperBound);
64
65              var constraint = new IntervalConstraint(expression, variable, parsedTarget, numberOfDerivation, interval, inclLowerBound, inclUpperBound, isEnabled);
66
67              yield return constraint;
68            }
69          } else {
70            throw new ArgumentException("The inserted target constraint is not valid!");
71          }
72          //Check for derivation
73        } else if (trimmedLine.StartsWith("d") || trimmedLine.StartsWith("\u2202")) {
74          var match = Regex.Match(trimmedLine,
75            @"([d∂])([²³]?)\s*(['](.*)[']|(.*[^\s]))\s*(\/)\s*([d∂])\s*(['](.*)[']|(.*[^\s²³]))\s*([²³]?)\s*\bin\b\s*([\[\]])\s*(\S*)\s*(\.{2})\s*(\S*)\s*([\[\]])");
76
77          if (match.Success) {
78            if (match.Groups.Count != 17) {
79              throw new ArgumentException("The given derivation-constraint is not complete");
80            } else {
81              var derivationTarget = match.Groups[3].Value.Trim();
82              var derivationVariable = match.Groups[8].Value.Trim();
83
84              if (match.Groups[3].Value.Trim().StartsWith("'") && match.Groups[3].Value.Trim().EndsWith("'")) {
85                derivationTarget = derivationTarget.Substring(1, derivationTarget.Length - 2);
86              }
87              if (match.Groups[8].Value.Trim().StartsWith("'") && match.Groups[8].Value.Trim().EndsWith("'")) {
88                derivationVariable = derivationVariable.Substring(1, derivationVariable.Length - 2);
89              }
90              if (target != "") {
91                if (derivationTarget != target) 
92                  throw new ArgumentException("The given target variable is not given in the dataset!");
93              }
94
95              if (variables != null && variables.Any()) {
96                if (variables.All(v => v != derivationVariable)) {
97                  throw new ArgumentException("The given variable does not exist in the dataset!");
98                }
99              }
100
101              if (match.Groups[2].Value.Trim() != "" || match.Groups[11].Value.Trim() != "") {
102                if (match.Groups[2].Value.Trim() == "" || match.Groups[11].Value.Trim() == "")
103                  throw new ArgumentException("Number of derivation has to be written on both sides!");
104                if (match.Groups[2].Value.Trim() != match.Groups[11].Value.Trim())
105                  throw new ArgumentException("Derivation number is not equal on both sides!");
106              }
107
108              var lowerBound = ParseIntervalBounds(match.Groups[13].Value);
109              var upperBound = ParseIntervalBounds(match.Groups[15].Value);
110              var expression = match.Groups[0].Value;
111              var parsedTarget = derivationTarget;
112              var isEnabled = true;
113              var inclLowerBound = match.Groups[12].Value.Trim() == "[";
114              var inclUpperBound = match.Groups[16].Value.Trim() == "]";
115              var variable = derivationVariable;
116              var numberOfDerivation = ParseDerivationCount(match.Groups[2].Value.Trim());
117              var interval = new Interval(lowerBound, upperBound);
118
119              var constraint = new IntervalConstraint(expression, variable, parsedTarget, numberOfDerivation, interval, inclLowerBound, inclUpperBound, isEnabled);
120
121              yield return constraint;
122            }
123          } else {
124            throw new ArgumentException("The inserted derivation constraint is not valid!");
125          }
126          //Check for comment
127        } else if (trimmedLine.StartsWith("#") || trimmedLine == "") {
128          //If it is a comment just continue without saving anything
129          continue;
130        } else {
131          throw new ArgumentException("Error at your constraints definition constraints have to start with (Target: | d | \u2202 | #)");
132        }
133      }
134    }
135
136    private static double ParseIntervalBounds(string input) {
137      input = input.ToLower();
138      switch (input) {
139        case "+inf.":
140        case "inf.":
141          return double.PositiveInfinity;
142        case "-inf.":
143          return double.NegativeInfinity;
144        default: {
145            if (double.TryParse(input, NumberStyles.Any, CultureInfo.InvariantCulture, out var value)) {
146              return value;
147            } else {
148              throw new ArgumentException("The given boundary is not a double value!");
149            }
150          }
151      }
152    }
153
154    private static int ParseDerivationCount(string input) {
155      switch (input) {
156        case "":
157          return 1;
158        case "²":
159          return 2;
160        case "³":
161          return 3;
162        default:
163          int.TryParse(input, out var value);
164          return value;
165      }
166    }
167  }
168}
Note: See TracBrowser for help on using the repository browser.