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

Last change on this file since 17146 was 17146, checked in by mkommend, 2 months ago

#2971: Renamed views and updated error messages in constraint parser.

File size: 8.0 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("Varialbes 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.");
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}.");
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 inclLowerBound = match.Groups[5].Value.Trim() == "[";
66              var inclUpperBound = match.Groups[9].Value.Trim() == "]";
67              var isEnabled = true;
68              var numberOfDerivation = 0;
69              var interval = new Interval(lowerBound, upperBound);
70
71              var constraint = new IntervalConstraint(expression, variable, parsedTarget, numberOfDerivation, interval, inclLowerBound, inclUpperBound, isEnabled);
72
73              yield return constraint;
74            }
75          } else {
76            throw new ArgumentException("The inserted target constraint is not valid.");
77          }
78          //Check for derivation
79        } else if (trimmedLine.StartsWith("d") || trimmedLine.StartsWith("\u2202")) {
80          var match = Regex.Match(trimmedLine,
81            @"([d∂])([²³]?)\s*(['](.*)[']|(.*[^\s]))\s*(\/)\s*([d∂])\s*(['](.*)[']|(.*[^\s²³]))\s*([²³]?)\s*\bin\b\s*([\[\]])\s*(\S*)\s*(\.{2})\s*(\S*)\s*([\[\]])");
82
83          if (match.Success) {
84            if (match.Groups.Count != 17) {
85              throw new ArgumentException("The given derivation-constraint is not complete.");
86            } else {
87              var derivationTarget = match.Groups[3].Value.Trim();
88              var derivationVariable = match.Groups[8].Value.Trim();
89
90              if (match.Groups[3].Value.Trim().StartsWith("'") && match.Groups[3].Value.Trim().EndsWith("'")) {
91                derivationTarget = derivationTarget.Substring(1, derivationTarget.Length - 2);
92              }
93              if (match.Groups[8].Value.Trim().StartsWith("'") && match.Groups[8].Value.Trim().EndsWith("'")) {
94                derivationVariable = derivationVariable.Substring(1, derivationVariable.Length - 2);
95              }
96
97              if (derivationTarget != target) {
98                throw new ArgumentException($"The target variable {derivationTarget}  does not match the provided target {target}.");
99              }
100
101              if (variables.All(v => v != derivationVariable)) {
102                throw new ArgumentException($"The given variable {derivationVariable} does not exist in the dataset.");
103              }
104
105              if (match.Groups[2].Value.Trim() != "" || match.Groups[11].Value.Trim() != "") {
106                if (match.Groups[2].Value.Trim() == "" || match.Groups[11].Value.Trim() == "")
107                  throw new ArgumentException("Number of derivation has to be written on both sides.");
108                if (match.Groups[2].Value.Trim() != match.Groups[11].Value.Trim())
109                  throw new ArgumentException("Derivation number is not equal on both sides.");
110              }
111
112              var lowerBound = ParseIntervalBounds(match.Groups[13].Value);
113              var upperBound = ParseIntervalBounds(match.Groups[15].Value);
114              var expression = match.Groups[0].Value;
115              var parsedTarget = derivationTarget;
116              var isEnabled = true;
117              var inclLowerBound = match.Groups[12].Value.Trim() == "[";
118              var inclUpperBound = match.Groups[16].Value.Trim() == "]";
119              var variable = derivationVariable;
120              var numberOfDerivation = ParseDerivationCount(match.Groups[2].Value.Trim());
121              var interval = new Interval(lowerBound, upperBound);
122
123              var constraint = new IntervalConstraint(expression, variable, parsedTarget, numberOfDerivation, interval, inclLowerBound, inclUpperBound, isEnabled);
124
125              yield return constraint;
126            }
127          } else {
128            throw new ArgumentException("The inserted derivation constraint is not valid.");
129          }
130          //Check for comment
131        } else if (trimmedLine.StartsWith("#") || trimmedLine == "") {
132          //If it is a comment just continue without saving anything
133          continue;
134        } else {
135          throw new ArgumentException("Error at your constraints definition constraints have to start with (Target: | d | \u2202 | #)");
136        }
137      }
138    }
139
140    private static double ParseIntervalBounds(string input) {
141      input = input.ToLower();
142      switch (input) {
143        case "+inf.":
144        case "inf.":
145          return double.PositiveInfinity;
146        case "-inf.":
147          return double.NegativeInfinity;
148        default: {
149            if (double.TryParse(input, NumberStyles.Any, CultureInfo.InvariantCulture, out var value)) {
150              return value;
151            } else {
152              throw new ArgumentException("The given boundary is not a double value!");
153            }
154          }
155      }
156    }
157
158    private static int ParseDerivationCount(string input) {
159      switch (input) {
160        case "":
161          return 1;
162        case "²":
163          return 2;
164        case "³":
165          return 3;
166        default:
167          int.TryParse(input, out var value);
168          return value;
169      }
170    }
171  }
172}
Note: See TracBrowser for help on using the repository browser.