1  #region License Information


2  /* HeuristicLab


3  * Copyright (C) 20022019 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  using System;


22  using System.Collections.Generic;


23  using System.Globalization;


24  using System.Linq;


25  using System.Text.RegularExpressions;


26 


27  namespace HeuristicLab.Problems.DataAnalysis {


28  public static class IntervalConstraintsParser {


29  public static IEnumerable<IntervalConstraint> Parse(string input) {


30  var options = RegexOptions.Multiline  RegexOptions.IgnoreCase;


31  var matches = Regex.Matches(input, @"^(.*)\bin\b\s*([\[\]])(.*[^\s])(\s*\.\.\s*)([^\s].*)([\[\]])\n*\r*\s*$", options);


32 


33  for (var i = 0; i < matches.Count; ++i) {


34  if (matches[i].Groups.Count == 7) {


35  var intervalConstraint = new IntervalConstraint();


36  //pattern 1 = defintion


37  var definition = Regex.Replace(matches[i].Groups[1].Value, @"\s *", "");


38  if (Regex.IsMatch(definition, @"\/")) {


39  var splitted = Regex.Split(definition.Replace(" ", string.Empty), @"\/");


40  var match = Regex.Match(splitted[0], @"([d∂])([09][²³])?(.*[^\s*])");


41  if (match.Success) {


42  intervalConstraint.NumberOfDerivation = match.Groups[2].Success ? ParseDerivationCount(match.Groups[2].Value) : 1;


43  intervalConstraint.Definition = match.Groups[3].Value;


44  intervalConstraint.IsDerivation = true;


45  var formulation = Regex.Match(splitted[1], @"([d∂])(.*[^²³])([²³])?");


46  if (formulation.Success) {


47  intervalConstraint.Variable = formulation.Groups[2].Success ? formulation.Groups[2].Value : "";


48  }


49  } else {


50  throw new ArgumentException($"An error occured in the derivation part: {splitted[0]}");


51  }


52  } else {


53  intervalConstraint.Definition = Regex.Match(definition, @".*[^.\s]*").Value;


54  intervalConstraint.IsDerivation = false;


55  }


56  intervalConstraint.Expression = matches[i].Groups[0].Value.Trim(' ', '\t', '\n', '\r');


57  intervalConstraint.InclusiveLowerBound = (matches[i].Groups[2].Value == "[");


58  intervalConstraint.InclusiveUpperBound = (matches[i].Groups[6].Value == "]");


59  intervalConstraint.Interval = new Interval(ParseIntervalBounds(matches[i].Groups[3].Value.Replace(" ", string.Empty)),


60  ParseIntervalBounds(matches[i].Groups[5].Value.Replace(" ", string.Empty)));


61 


62  yield return intervalConstraint;


63  } else {


64  throw new ArgumentException($"The given constraint: {matches[i].Value} is not valid.");


65  }


66  }


67  }


68 


69 


70  public static IEnumerable<IntervalConstraint> ParseInput(string input, string target = "", IEnumerable<string> variables = null) {


71  var lines = input.Split(new[] { "\r\n", "\r", "\n" }, StringSplitOptions.None);


72  foreach (var line in lines) {


73  var trimmedLine = line.TrimStart();


74  //Check for targetvariable constraint


75  if (trimmedLine.StartsWith("Target:")) {


76  var start = "Target:".Length;


77  var end = trimmedLine.Length;


78  var targetConstraint = trimmedLine.Substring(start, endstart);


79  var match = Regex.Match(targetConstraint,


80  @"['](.*)[']\s*(\bin\b)\s*([\[\]])\s*(\S*)\s*(..)\s*(\S*)\s*([\[\]])");


81  if (match.Success) {


82  if (match.Groups.Count != 8) {


83  throw new ArgumentException("The given targetconstraint is not complete!");


84  } else {


85  if (target != "") {


86  if (match.Groups[1].Value.Trim() != target) {


87  throw new ArgumentException("The given target variable is not in the given dataset!");


88  }


89  }


90  var lowerBound = ParseIntervalBounds(match.Groups[4].Value);


91  var upperBound = ParseIntervalBounds(match.Groups[6].Value);


92  var constraint = new IntervalConstraint("Target:" + match.Groups[0].Value);


93  constraint.Expression = "Target:" + match.Groups[0].Value;


94  constraint.Definition = "Target " + match.Groups[1].Value.Trim();


95  constraint.Variable = match.Groups[1].Value.Trim();


96  constraint.InclusiveLowerBound = match.Groups[3].Value.Trim() == "[";


97  constraint.InclusiveUpperBound = match.Groups[7].Value.Trim() == "]";


98  constraint.IsDerivation = false;


99  constraint.NumberOfDerivation = 0;


100  constraint.Interval = new Interval(lowerBound, upperBound);


101 


102  yield return constraint;


103  }


104  } else {


105  throw new ArgumentException("The inserted target constraint is not valid!");


106  }


107  //Check for derivation


108  } else if (trimmedLine.StartsWith("d")  trimmedLine.StartsWith("\u2202")) {


109  var match = Regex.Match(trimmedLine,


110  @"([d∂])([²³]?)\s*['](.*)[']\s*(\/)\s*([d∂])\s*['](.*)[']\s*([²³]?)\s*\bin\b\s*([\[\]])\s*(\S*)\s*(..)\s*(\S*)\s*([\[\]])");


111 


112  if (match.Success) {


113  if (match.Groups.Count != 13) {


114  throw new ArgumentException("The given derivationconstraint is not complete");


115  } else {


116  if (target != "") {


117  if (match.Groups[3].Value != target)


118  throw new ArgumentException("The given target variable is not given in the dataset!");


119  }


120 


121  if (variables != null && variables.Any()) {


122  if (variables.All(v => v != match.Groups[6].Value.Trim())) {


123  throw new ArgumentException("The given variable does not exist in the dataset!");


124  }


125  }


126 


127  if (match.Groups[2].Value.Trim() != ""  match.Groups[7].Value.Trim() != "") {


128  if (match.Groups[2].Value.Trim() == ""  match.Groups[7].Value.Trim() == "")


129  throw new ArgumentException("Number of derivation has to be written on both sides!");


130  if (match.Groups[2].Value.Trim() != match.Groups[7].Value.Trim())


131  throw new ArgumentException("Derivation number is not equal on both sides!");


132  }


133 


134  var lowerBound = ParseIntervalBounds(match.Groups[9].Value);


135  var upperBound = ParseIntervalBounds(match.Groups[11].Value);


136  var constraint = new IntervalConstraint(match.Groups[0].Value);


137  constraint.Expression = match.Groups[0].Value;


138  constraint.Definition = match.Groups[1].Value + match.Groups[2].Value + match.Groups[3].Value +


139  match.Groups[4].Value + match.Groups[5].Value + match.Groups[6].Value + match.Groups[7].Value;


140  constraint.IsDerivation = true;


141  constraint.InclusiveLowerBound = match.Groups[8].Value.Trim() == "[";


142  constraint.InclusiveUpperBound = match.Groups[12].Value.Trim() == "]";


143  constraint.Variable = match.Groups[6].Value.Trim();


144  constraint.NumberOfDerivation = ParseDerivationCount(match.Groups[2].Value.Trim());


145  constraint.Interval = new Interval(lowerBound, upperBound);


146 


147  yield return constraint;


148  }


149  } else {


150  throw new ArgumentException("The inserted derivation constraint is not valid!");


151  }


152  //Check for comment


153  } else if (trimmedLine.StartsWith("#")  trimmedLine == "") {


154  //If it is a comment just continue without saving anything


155  continue;


156  } else {


157  throw new ArgumentException("Error at your constraints definition constraints have to start with (Target:  d  \u2202  #)");


158  }


159  }


160  }


161 


162  private static double ParseIntervalBounds(string input) {


163  input = input.ToLower();


164  switch (input) {


165  case "+inf.":


166  case "inf.":


167  return double.PositiveInfinity;


168  case "inf.":


169  return double.NegativeInfinity;


170  default: {


171  if (double.TryParse(input, NumberStyles.Any, CultureInfo.InvariantCulture, out var value)) {


172  return value;


173  } else {


174  throw new ArgumentException("The given boundary is not a double value!");


175  }


176  }


177  }


178  }


179 


180  private static int ParseDerivationCount(string input) {


181  switch (input) {


182  case "":


183  return 1;


184  case "²":


185  return 2;


186  case "³":


187  return 3;


188  default:


189  int.TryParse(input, out var value);


190  return value;


191  }


192  }


193  }


194  }

