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

Last change on this file since 17370 was 17370, checked in by chaider, 11 months ago

#2971 Removed the possibility of declaring open and closed intervals. All intervals are closed intervals now.

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 inputText, string target, IEnumerable<string> variables) {
31
32      if (string.IsNullOrEmpty(inputText)) throw new ArgumentNullException("No input text has been provided.");
33      if (string.IsNullOrEmpty(target)) throw new ArgumentNullException("No target variable has been provided.");
34      if (variables == null) throw new ArgumentNullException("No variables have been provided.");
35      if (!variables.Any()) throw new ArgumentException("Variables are empty.");
36
37      var lines = inputText.Split(new[] { "\r\n", "\r", "\n" }, StringSplitOptions.None);
38      foreach (var line in lines) {
39        var trimmedLine = line.TrimStart();
40        //Check for target-variable constraint
41        if (trimmedLine.StartsWith("Target:")) {
42          var start = "Target:".Length;
43          var end = trimmedLine.Length;
44          var targetConstraint = trimmedLine.Substring(start, end - start);
45          var match = Regex.Match(targetConstraint,
46            @"(['](.*)[']|(.*[^\s]))\s*(\bin\b)\s*([\[])\s*(\S*)\s*(\.{2})\s*(\S*)\s*([\]])");
47          if (match.Success) {
48            if (match.Groups.Count != 10) {
49              throw new ArgumentException("The target-constraint is not complete.", line);
50            } else {
51              var targetVariable = match.Groups[1].Value.Trim();
52              if (match.Groups[1].Value.Trim().StartsWith("'") && match.Groups[1].Value.Trim().EndsWith("'")) {
53                targetVariable = targetVariable.Substring(1, targetVariable.Length - 2);
54              }
55
56              if (targetVariable != target) {
57                throw new ArgumentException($"The target variable {targetVariable}  does not match the provided target {target}.", line);
58              }
59
60              var lowerBound = ParseIntervalBounds(match.Groups[6].Value);
61              var upperBound = ParseIntervalBounds(match.Groups[8].Value);
62              var expression = "Target:" + match.Groups[0].Value;
63              var parsedTarget = match.Groups[1].Value.Trim();
64              var variable = targetVariable;
65              var isEnabled = true;
66              var numberOfDerivation = 0;
67              var interval = new Interval(lowerBound, upperBound);
68
69              var constraint = new IntervalConstraint(expression, variable, parsedTarget, numberOfDerivation, interval, isEnabled);
70
71              yield return constraint;
72            }
73          } else {
74            throw new ArgumentException("The inserted target constraint is not valid.", line);
75          }
76          //Check for derivation
77        } else if (trimmedLine.StartsWith("d") || trimmedLine.StartsWith("\u2202")) {
78          var match = Regex.Match(trimmedLine,
79            @"([d∂])([²³]?)\s*(['](.*)[']|(.*[^\s]))\s*(\/)\s*([d∂])\s*(['](.*)[']|(.*[^\s²³]))\s*([²³]?)\s*\bin\b\s*([\[])\s*(\S*)\s*(\.{2})\s*(\S*)\s*([\]])");
80
81          if (match.Success) {
82            if (match.Groups.Count != 17) {
83              throw new ArgumentException("The given derivation-constraint is not complete.", line);
84            } else {
85              var derivationTarget = match.Groups[3].Value.Trim();
86              var derivationVariable = match.Groups[8].Value.Trim();
87
88              if (match.Groups[3].Value.Trim().StartsWith("'") && match.Groups[3].Value.Trim().EndsWith("'")) {
89                derivationTarget = derivationTarget.Substring(1, derivationTarget.Length - 2);
90              }
91              if (match.Groups[8].Value.Trim().StartsWith("'") && match.Groups[8].Value.Trim().EndsWith("'")) {
92                derivationVariable = derivationVariable.Substring(1, derivationVariable.Length - 2);
93              }
94
95              if (derivationTarget != target) {
96                throw new ArgumentException($"The target variable {derivationTarget}  does not match the provided target {target}.", line);
97              }
98
99              if (variables.All(v => v != derivationVariable)) {
100                throw new ArgumentException($"The given variable {derivationVariable} does not exist in the dataset.", line);
101              }
102
103              if (match.Groups[2].Value.Trim() != "" || match.Groups[11].Value.Trim() != "") {
104                if (match.Groups[2].Value.Trim() == "" || match.Groups[11].Value.Trim() == "")
105                  throw new ArgumentException("Number of derivation has to be written on both sides.", line);
106                if (match.Groups[2].Value.Trim() != match.Groups[11].Value.Trim())
107                  throw new ArgumentException("Derivation number is not equal on both sides.", line);
108              }
109
110              var lowerBound = ParseIntervalBounds(match.Groups[13].Value);
111              var upperBound = ParseIntervalBounds(match.Groups[15].Value);
112              var expression = match.Groups[0].Value;
113              var parsedTarget = derivationTarget;
114              var isEnabled = true;
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, isEnabled);
120
121              yield return constraint;
122            }
123          } else {
124            throw new ArgumentException("The inserted derivation constraint is not valid.", line);
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 | #)", line);
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.