Index: /branches/3073_IA_constraint_splitting/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/HeuristicLab.Problems.DataAnalysis.Symbolic-3.4.csproj
===================================================================
--- /branches/3073_IA_constraint_splitting/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/HeuristicLab.Problems.DataAnalysis.Symbolic-3.4.csproj (revision 17767)
+++ /branches/3073_IA_constraint_splitting/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/HeuristicLab.Problems.DataAnalysis.Symbolic-3.4.csproj (revision 17768)
@@ -242,4 +242,5 @@
+
Index: /branches/3073_IA_constraint_splitting/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/Interfaces/IBoundsEstimator.cs
===================================================================
--- /branches/3073_IA_constraint_splitting/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/Interfaces/IBoundsEstimator.cs (revision 17767)
+++ /branches/3073_IA_constraint_splitting/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/Interfaces/IBoundsEstimator.cs (revision 17768)
@@ -16,5 +16,10 @@
ISymbolicExpressionTree tree, IntervalCollection variableRanges);
+ double CheckConstraint(
+ ISymbolicExpressionTree tree, IntervalCollection variableRanges, IntervalConstraint constraint);
+
+ bool IsCompatible(ISymbolicExpressionTree tree);
+
int EvaluatedSolutions { get; set; }
- }
+ }
}
Index: /branches/3073_IA_constraint_splitting/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/Interpreter/IABoundsEstimator.cs
===================================================================
--- /branches/3073_IA_constraint_splitting/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/Interpreter/IABoundsEstimator.cs (revision 17767)
+++ /branches/3073_IA_constraint_splitting/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/Interpreter/IABoundsEstimator.cs (revision 17768)
@@ -3,6 +3,4 @@
using System.Collections.ObjectModel;
using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
using HEAL.Attic;
using HeuristicLab.Common;
@@ -15,5 +13,5 @@
[StorableType("C8539434-6FB0-47D0-9F5A-2CAE5D8B8B4F")]
[Item("IA Bounds Estimator", "Interpreter for calculation of intervals of symbolic models.")]
- public sealed class IABoundsEstimator : ParameterizedNamedItem, IBoundsEstimator{
+ public sealed class IABoundsEstimator : ParameterizedNamedItem, IBoundsEstimator {
#region Parameters
@@ -54,4 +52,5 @@
set => SplittingWidthParameter.Value.Value = value;
}
+
#endregion
@@ -60,12 +59,17 @@
[StorableConstructor]
private IABoundsEstimator(StorableConstructorFlag _) : base(_) { }
-
+
private IABoundsEstimator(IABoundsEstimator original, Cloner cloner) : base(original, cloner) { }
- public IABoundsEstimator() : base("IA Bounds Estimator", "Estimates the bounds of the model with interval arithmetic") {
- Parameters.Add(new FixedValueParameter(EvaluatedSolutionsParameterName, "A counter for the total number of solutions the estimator has evaluated.", new IntValue(0)));
- Parameters.Add(new FixedValueParameter(UseIntervalSplittingParameterName, "Defines whether interval splitting is activated or not.", new BoolValue(false)));
- Parameters.Add(new FixedValueParameter(SplittingIterationsParameterName, "Defines the number of iterations of splitting.", new IntValue(200)));
- Parameters.Add(new FixedValueParameter(SplittingWidthParameterName, "Width of interval, after the splitting should stop.", new DoubleValue(0.0)));
+ public IABoundsEstimator() : base("IA Bounds Estimator",
+ "Estimates the bounds of the model with interval arithmetic") {
+ Parameters.Add(new FixedValueParameter(EvaluatedSolutionsParameterName,
+ "A counter for the total number of solutions the estimator has evaluated.", new IntValue(0)));
+ Parameters.Add(new FixedValueParameter(UseIntervalSplittingParameterName,
+ "Defines whether interval splitting is activated or not.", new BoolValue(false)));
+ Parameters.Add(new FixedValueParameter(SplittingIterationsParameterName,
+ "Defines the number of iterations of splitting.", new IntValue(200)));
+ Parameters.Add(new FixedValueParameter(SplittingWidthParameterName,
+ "Width of interval, after the splitting should stop.", new DoubleValue(0.0)));
}
@@ -74,5 +78,5 @@
}
- #endregion
+ #endregion
#region IStatefulItem Members
@@ -86,5 +90,5 @@
public void ClearState() { }
- #endregion
+ #endregion
#region Evaluation
@@ -261,9 +265,10 @@
}
- #endregion
+ #endregion
#region Helpers
- private static IDictionary GetOccurringVariableRanges(ISymbolicExpressionTree tree, IntervalCollection variableRanges) {
+ private static IDictionary GetOccurringVariableRanges(
+ ISymbolicExpressionTree tree, IntervalCollection variableRanges) {
var variables = tree.IterateNodesPrefix().OfType().Select(v => v.VariableName).Distinct()
.ToList();
@@ -321,9 +326,13 @@
public static Interval EvaluateWithSplitting(Instruction[] instructions,
IDictionary variableIntervals,
- List multipleOccurenceVariables, int splittingIterations, double splittingWidth, IDictionary nodeIntervals = null) {
- var savedIntervals = variableIntervals.ToDictionary(entry => entry.Key, entry => entry.Value);
- var min = FindBound(instructions, variableIntervals, multipleOccurenceVariables, splittingIterations, splittingWidth, nodeIntervals,
+ List multipleOccurenceVariables, int splittingIterations,
+ double splittingWidth,
+ IDictionary nodeIntervals =
+ null) {
+ var min = FindBound(instructions, variableIntervals.ToDictionary(entry => entry.Key, entry => entry.Value),
+ multipleOccurenceVariables, splittingIterations, splittingWidth, nodeIntervals,
minimization: true);
- var max = FindBound(instructions, savedIntervals, multipleOccurenceVariables, splittingIterations, splittingWidth, nodeIntervals,
+ var max = FindBound(instructions, variableIntervals.ToDictionary(entry => entry.Key, entry => entry.Value),
+ multipleOccurenceVariables, splittingIterations, splittingWidth, nodeIntervals,
minimization: false);
@@ -333,12 +342,13 @@
private static double FindBound(Instruction[] instructions,
IDictionary variableIntervals,
- List multipleOccurenceVariables, int splittingIterations, double splittingWidth, IDictionary nodeIntervals = null, bool minimization = true) {
+ List multipleOccurenceVariables, int splittingIterations,
+ double splittingWidth,
+ IDictionary nodeIntervals = null,
+ bool minimization = true, bool stopAtLimit = false, double limit = 0) {
SortedSet prioQ = new SortedSet();
-
var ic = 0;
+ var stop = false;
//Calculate full box
- //IReadOnlyDictionary readonlyRanges =
- // variableIntervals.ToDictionary(k => k.Key, k => k.Value);
- var interval = Evaluate(instructions, ref ic, nodeIntervals, variableIntervals:variableIntervals);
+ var interval = Evaluate(instructions, ref ic, nodeIntervals, variableIntervals: variableIntervals);
// the order of keys in a dictionary is guaranteed to be the same order as values in a dictionary
// https://docs.microsoft.com/en-us/dotnet/api/system.collections.idictionary.keys?view=netcore-3.1#remarks
@@ -348,11 +358,13 @@
if (minimization) {
prioQ.Add(new BoxBound(box, interval.LowerBound));
+ if (stopAtLimit && interval.LowerBound >= limit) stop = true;
} else {
prioQ.Add(new BoxBound(box, -interval.UpperBound));
+ if (stopAtLimit && interval.UpperBound <= limit) stop = true;
}
var discardedBound = double.MaxValue;
var runningBound = double.MaxValue;
- for (var depth = 0; depth < splittingIterations && prioQ.Count > 0; ++depth) {
+ for (var depth = 0; depth < splittingIterations && prioQ.Count > 0 && !stop; ++depth) {
var currentBound = prioQ.Min;
prioQ.Remove(currentBound);
@@ -382,9 +394,5 @@
var res = Evaluate(instructions, ref ic, nodeIntervals,
new ReadOnlyDictionary(variableIntervals));
- //if (minimization) {
- // prioQ.Add(new BoxBound(newBox, res.LowerBound));
- //} else {
- // prioQ.Add(new BoxBound(newBox, -res.UpperBound));
- //}
+
var boxBound = new BoxBound(newBox, minimization ? res.LowerBound : -res.UpperBound);
prioQ.Add(boxBound);
@@ -394,4 +402,11 @@
runningBound = innerBound;
+ if (minimization) {
+ if (stopAtLimit && innerBound >= limit)
+ stop = true;
+ } else {
+ if (stopAtLimit && innerBound <= limit)
+ stop = true;
+ }
}
@@ -413,5 +428,5 @@
private static IEnumerable> Split(List box, double minWidth) {
- List toList(Tuple t) => new List{t.Item1, t.Item2};
+ List toList(Tuple t) => new List {t.Item1, t.Item2};
var boxes = box.Select(region => region.Width > minWidth ? toList(region.Split()) : new List {region})
.ToList();
@@ -435,5 +450,6 @@
} else {
var vars = ContainsVariableMultipleTimes(tree, out var variables);
- resultInterval = EvaluateWithSplitting(instructions, occuringVariableRanges, variables, SplittingIterations, SplittingWidth);
+ resultInterval = EvaluateWithSplitting(instructions, occuringVariableRanges, variables, SplittingIterations,
+ SplittingWidth);
}
@@ -445,9 +461,89 @@
}
- public IDictionary GetModelNodesBounds(ISymbolicExpressionTree tree, IntervalCollection variableRanges) {
+ public IDictionary GetModelNodesBounds(
+ ISymbolicExpressionTree tree, IntervalCollection variableRanges) {
throw new NotImplementedException();
}
-
+ public double CheckConstraint(
+ ISymbolicExpressionTree tree, IntervalCollection variableRanges, IntervalConstraint constraint) {
+ var occuringVariableRanges = GetOccurringVariableRanges(tree, variableRanges);
+ var instructions = PrepareInterpreterState(tree, occuringVariableRanges);
+ if (!UseIntervalSplitting) {
+ var instructionCounter = 0;
+ var modelBound = Evaluate(instructions, ref instructionCounter, variableIntervals: occuringVariableRanges);
+ if (constraint.Interval.Contains(modelBound)) return 0.0;
+ return Math.Abs(modelBound.LowerBound - constraint.Interval.LowerBound) +
+ Math.Abs(modelBound.UpperBound - constraint.Interval.UpperBound);
+ }
+
+ if (double.IsNegativeInfinity(constraint.Interval.LowerBound) &&
+ double.IsPositiveInfinity(constraint.Interval.UpperBound)) {
+ return 0.0;
+ }
+
+ ContainsVariableMultipleTimes(tree, out var variables);
+
+ var upperBound = 0.0;
+ var lowerBound = 0.0;
+ if (double.IsNegativeInfinity(constraint.Interval.LowerBound)) {
+ upperBound = FindBound(instructions, occuringVariableRanges, variables, SplittingIterations, SplittingWidth,
+ minimization: false, stopAtLimit: true, limit: constraint.Interval.UpperBound);
+
+ return upperBound <= constraint.Interval.UpperBound
+ ? 0.0
+ : Math.Abs(upperBound - constraint.Interval.UpperBound);
+ }
+
+ if (double.IsPositiveInfinity(constraint.Interval.UpperBound)) {
+ lowerBound = FindBound(instructions, occuringVariableRanges, variables, SplittingIterations, SplittingWidth,
+ minimization: true, stopAtLimit: true, limit: constraint.Interval.LowerBound);
+
+ return lowerBound <= constraint.Interval.LowerBound
+ ? 0.0
+ : Math.Abs(lowerBound - constraint.Interval.LowerBound);
+ }
+
+ upperBound = FindBound(instructions, occuringVariableRanges, variables, SplittingIterations, SplittingWidth,
+ minimization: false, stopAtLimit: true, limit: constraint.Interval.UpperBound);
+ lowerBound = FindBound(instructions, occuringVariableRanges, variables, SplittingIterations, SplittingWidth,
+ minimization: true, stopAtLimit: true, limit: constraint.Interval.LowerBound);
+
+
+ var res = 0.0;
+
+ res += upperBound <= constraint.Interval.UpperBound ? 0.0 : Math.Abs(upperBound - constraint.Interval.UpperBound);
+ res += lowerBound <= constraint.Interval.LowerBound ? 0.0 : Math.Abs(lowerBound - constraint.Interval.LowerBound);
+
+ return res;
+ }
+
+
+ public bool IsCompatible(ISymbolicExpressionTree tree) {
+ var containsUnknownSymbols = (
+ from n in tree.Root.GetSubtree(0).IterateNodesPrefix()
+ where
+ !(n.Symbol is Variable) &&
+ !(n.Symbol is Constant) &&
+ !(n.Symbol is StartSymbol) &&
+ !(n.Symbol is Addition) &&
+ !(n.Symbol is Subtraction) &&
+ !(n.Symbol is Multiplication) &&
+ !(n.Symbol is Division) &&
+ !(n.Symbol is Sine) &&
+ !(n.Symbol is Cosine) &&
+ !(n.Symbol is Tangent) &&
+ !(n.Symbol is HyperbolicTangent) &&
+ !(n.Symbol is Logarithm) &&
+ !(n.Symbol is Exponential) &&
+ !(n.Symbol is Square) &&
+ !(n.Symbol is SquareRoot) &&
+ !(n.Symbol is Cube) &&
+ !(n.Symbol is CubeRoot) &&
+ !(n.Symbol is Absolute) &&
+ !(n.Symbol is AnalyticQuotient)
+ select n).Any();
+ return !containsUnknownSymbols;
+ }
}
}
Index: /branches/3073_IA_constraint_splitting/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/IntervalUtil.cs
===================================================================
--- /branches/3073_IA_constraint_splitting/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/IntervalUtil.cs (revision 17768)
+++ /branches/3073_IA_constraint_splitting/HeuristicLab.Problems.DataAnalysis.Symbolic/3.4/IntervalUtil.cs (revision 17768)
@@ -0,0 +1,87 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using HeuristicLab.Encodings.SymbolicExpressionTreeEncoding;
+
+namespace HeuristicLab.Problems.DataAnalysis.Symbolic {
+ public static class IntervalUtil {
+ public static double IntervalConstraintViolation(
+ IntervalConstraint constraint, IBoundsEstimator estimator, IntervalCollection intervalCollection,
+ ISymbolicExpressionTree solution) {
+ var variableRanges = intervalCollection.GetReadonlyDictionary();
+
+ if (constraint.Variable != null && !variableRanges.ContainsKey(constraint.Variable)) {
+ throw new ArgumentException(
+ $"The given variable {constraint.Variable} in the constraint does not exist in the model.",
+ nameof(IntervalConstraintsParser));
+ }
+
+ //Create new variable ranges for defined regions
+ var regionRanges = new IntervalCollection();
+ foreach (var kvp in variableRanges) {
+ if (kvp.Key != constraint.Target && constraint.Regions.GetReadonlyDictionary().TryGetValue(kvp.Key, out Interval val)) {
+ regionRanges.AddInterval(kvp.Key, val);
+ } else {
+ regionRanges.AddInterval(kvp.Key, kvp.Value);
+ }
+ }
+
+ var error = 0.0;
+ if (!constraint.IsDerivative) {
+ error = estimator.CheckConstraint(solution, regionRanges, constraint);
+ } else {
+ var tree = solution;
+ for (var i = 0; i < constraint.NumberOfDerivations; ++i) {
+ if (!estimator.IsCompatible(tree) || !DerivativeCalculator.IsCompatible(tree)) {
+ throw new ArgumentException("Cube, Root, Power symbols are not supported.");
+ }
+
+ tree = DerivativeCalculator.Derive(tree, constraint.Variable);
+ }
+
+ error = estimator.CheckConstraint(tree, regionRanges, constraint);
+ }
+
+ return error * constraint.Weight;
+ //Interval resultInterval;
+ //if (!constraint.IsDerivative) {
+ // resultInterval = estimator.GetModelBound(solution, regionRanges);
+ //} else {
+ // var tree = solution;
+ // for (var i = 0; i < constraint.NumberOfDerivations; ++i) {
+ // if (!estimator.IsCompatible(tree) || !DerivativeCalculator.IsCompatible(tree)) {
+ // throw new ArgumentException("Cube, Root, Power symbols are not supported.");
+ // }
+
+ // tree = DerivativeCalculator.Derive(tree, constraint.Variable);
+ // }
+
+ // resultInterval = estimator.GetModelBound(tree, regionRanges);
+ //}
+
+ //var error = 0.0;
+
+ //if (!constraint.Interval.Contains(resultInterval.LowerBound)) {
+ // error += Math.Abs(resultInterval.LowerBound - constraint.Interval.LowerBound);
+ //}
+
+ //if (!constraint.Interval.Contains(resultInterval.UpperBound)) {
+ // error += Math.Abs(resultInterval.UpperBound - constraint.Interval.UpperBound);
+ //}
+
+ //error *= constraint.Weight;
+
+ //return error;
+ }
+
+ public static IEnumerable IntervalConstraintsViolation(
+ IEnumerable constraints, IBoundsEstimator estimator, IntervalCollection intervalCollection,
+ ISymbolicExpressionTree solution) {
+ return constraints
+ .Select(constraint => IntervalConstraintViolation(constraint, estimator, intervalCollection, solution))
+ .ToList();
+ }
+ }
+}
Index: /branches/3073_IA_constraint_splitting/HeuristicLab.Problems.DataAnalysis/3.4/HeuristicLab.Problems.DataAnalysis-3.4.csproj
===================================================================
--- /branches/3073_IA_constraint_splitting/HeuristicLab.Problems.DataAnalysis/3.4/HeuristicLab.Problems.DataAnalysis-3.4.csproj (revision 17767)
+++ /branches/3073_IA_constraint_splitting/HeuristicLab.Problems.DataAnalysis/3.4/HeuristicLab.Problems.DataAnalysis-3.4.csproj (revision 17768)
@@ -192,5 +192,4 @@
-
Index: /branches/3073_IA_constraint_splitting/HeuristicLab.Problems.DataAnalysis/3.4/Implementation/Interval/IntervalConstraint.cs
===================================================================
--- /branches/3073_IA_constraint_splitting/HeuristicLab.Problems.DataAnalysis/3.4/Implementation/Interval/IntervalConstraint.cs (revision 17767)
+++ /branches/3073_IA_constraint_splitting/HeuristicLab.Problems.DataAnalysis/3.4/Implementation/Interval/IntervalConstraint.cs (revision 17768)
@@ -115,6 +115,6 @@
}
- [Storable] private IEnumerable regions;
- public IEnumerable Regions {
+ [Storable] private IntervalCollection regions;
+ public IntervalCollection Regions {
get => regions;
set {
@@ -146,8 +146,8 @@
Interval interval, double weight, bool enabled)
: this(expression, variable, target, numberOfDerivations,
- interval, Enumerable.Empty(), weight, enabled) { }
+ interval, new IntervalCollection(), weight, enabled) { }
public IntervalConstraint(string expression, string variable, string target, int numberOfDerivations,
- Interval interval, IEnumerable regions, double weight, bool enabled) {
+ Interval interval, IntervalCollection regions, double weight, bool enabled) {
this.regions = regions;
this.weight = weight;
@@ -166,5 +166,5 @@
private IntervalConstraint(IntervalConstraint original, Cloner cloner)
: base(original, cloner) {
- Regions = new List(original.Regions?.Select(r => cloner.Clone(r)) ?? Enumerable.Empty());
+ Regions = original.Regions;
Expression = original.Expression;
Variable = original.Variable;
@@ -207,6 +207,6 @@
"]");
if(Regions != null) {
- foreach (var region in Regions)
- expression += $", {region.VariableName}=({region.Interval.LowerBound} .. {region.Interval.UpperBound})";
+ foreach (var region in Regions.GetReadonlyDictionary())
+ expression += $", {region.Key}=({region.Value.LowerBound} .. {region.Value.UpperBound})";
}
@@ -224,6 +224,6 @@
GetDerivationString(numberOfDerivations));
if (Regions != null) {
- foreach (var region in Regions)
- expression += $", {region.VariableName}=({region.Interval.LowerBound} .. {region.Interval.UpperBound})";
+ foreach (var region in Regions.GetReadonlyDictionary())
+ expression += $", {region.Key}=({region.Value.LowerBound} .. {region.Value.UpperBound})";
}
Expression = expression;
Index: /branches/3073_IA_constraint_splitting/HeuristicLab.Problems.DataAnalysis/3.4/Implementation/Interval/IntervalConstraintsParser.cs
===================================================================
--- /branches/3073_IA_constraint_splitting/HeuristicLab.Problems.DataAnalysis/3.4/Implementation/Interval/IntervalConstraintsParser.cs (revision 17767)
+++ /branches/3073_IA_constraint_splitting/HeuristicLab.Problems.DataAnalysis/3.4/Implementation/Interval/IntervalConstraintsParser.cs (revision 17768)
@@ -93,14 +93,14 @@
if (match.Groups[10].Success)
{
- IList regions = new List();
+ IntervalCollection regions = new IntervalCollection();
// option variables found
for(int idx = 0; idx < match.Groups[10].Captures.Count; ++idx)
{
- Region region = ParseRegion(
+ KeyValuePair region = ParseRegion(
match.Groups[11].Captures[idx].Value,
match.Groups[13].Captures[idx].Value,
match.Groups[15].Captures[idx].Value);
- if(!regions.Any(r => r.VariableName == region.VariableName))
- regions.Add(region);
+ if (regions.GetReadonlyDictionary().All(r => r.Key != region.Key))
+ regions.AddInterval(region.Key, region.Value);
else
throw new ArgumentException("A constraint cannot contain multiple regions of the same variable.");
@@ -184,14 +184,14 @@
if(match.Groups[17].Success)
{
- IList regions = new List();
+ IntervalCollection regions = new IntervalCollection();
// option variables found
for (int idx = 0; idx < match.Groups[17].Captures.Count; ++idx)
{
- Region region = ParseRegion(
+ KeyValuePair region = ParseRegion(
match.Groups[18].Captures[idx].Value,
match.Groups[20].Captures[idx].Value,
match.Groups[22].Captures[idx].Value);
- if (!regions.Any(r => r.VariableName == region.VariableName))
- regions.Add(region);
+ if (regions.GetReadonlyDictionary().All(r => r.Key != region.Key))
+ regions.AddInterval(region.Key, region.Value);
else
throw new ArgumentException("A constraint cannot contain multiple regions of the same variable.");
@@ -217,9 +217,10 @@
}
- private static Region ParseRegion(string variable, string lb, string ub)
+ private static KeyValuePair ParseRegion(string variable, string lb, string ub)
{
var regionLb = ParseIntervalBounds(lb);
var regionUb = ParseIntervalBounds(ub);
- return new Region(variable, new Interval(regionLb, regionUb));
+ return new KeyValuePair(variable, new Interval(regionLb, regionUb));
+ //return new Region(variable, new Interval(regionLb, regionUb));
}
Index: anches/3073_IA_constraint_splitting/HeuristicLab.Problems.DataAnalysis/3.4/Implementation/Interval/Region.cs
===================================================================
--- /branches/3073_IA_constraint_splitting/HeuristicLab.Problems.DataAnalysis/3.4/Implementation/Interval/Region.cs (revision 17767)
+++ (revision )
@@ -1,58 +1,0 @@
-using HEAL.Attic;
-using HeuristicLab.Common;
-using HeuristicLab.Core;
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-
-namespace HeuristicLab.Problems.DataAnalysis {
- [StorableType("8E3FEB9B-0B1A-4F06-8CFC-CB8D468EF7EC")]
- [Item("Region", "A region for an interval constraint.")]
- public class Region : Item {
-
- [Storable] private string variableName;
- public string VariableName {
- get => variableName;
- private set {
- if(variableName == null || variableName != value) {
- variableName = value;
- OnChanged();
- }
- }
- }
-
- [Storable] private Interval interval;
- public Interval Interval {
- get => interval;
- private set {
- if (interval == null || !interval.Equals(value)) {
- interval = value;
- OnChanged();
- }
- }
- }
-
- [StorableConstructor]
- private Region(StorableConstructorFlag _) : base(_) { }
-
- public Region(string variableName, Interval interval) {
- VariableName = variableName;
- Interval = interval;
- }
-
- private Region(Region original, Cloner cloner)
- : base(original, cloner) {
- VariableName = original.VariableName;
- Interval = original.Interval;
- }
-
- public override IDeepCloneable Clone(Cloner cloner) {
- return new Region(this, cloner);
- }
-
- public event EventHandler Changed;
- private void OnChanged() => Changed?.Invoke(this, EventArgs.Empty);
- }
-}