LotSizingDataModel.Instance 2.0.1
Lot-sizing instance representation, descriptors and problem characterization.
Loading...
Searching...
No Matches
KnownProblemRuleDefinition.cs
Go to the documentation of this file.
1using System;
2using System.Collections.Generic;
3using System.Linq;
4using System.Xml.Serialization;
5using LotSizingDataModel.Core.Common;
6
8
9/// <summary>
10/// Defines one reusable condition used to classify
11/// lot-sizing problem instances.
12/// </summary>
13/// <remarks>
14/// A rule definition identifies a property of
15/// <see cref="LotSizingProblemFeatures"/>, a comparison
16/// operator and an optional expected value.
17///
18/// The rule definition does not contain an evaluation result.
19/// Evaluating a rule against an instance produces a
20/// <see cref="ClassificationEvidence"/> object.
21/// </remarks>
22[Serializable]
23[XmlType(TypeName = "knownProblemRuleDefinition")]
24public sealed class KnownProblemRuleDefinition : ModelObject
25{
26 /// <summary>
27 /// Identifies an equality comparison.
28 /// </summary>
29 public const string EqualsOperator = "EQUALS";
30
31 /// <summary>
32 /// Identifies an inequality comparison.
33 /// </summary>
34 public const string NotEqualsOperator = "NOT_EQUALS";
35
36 /// <summary>
37 /// Identifies a strict greater-than comparison.
38 /// </summary>
39 public const string GreaterThanOperator = "GREATER_THAN";
40
41 /// <summary>
42 /// Identifies a greater-than-or-equal comparison.
43 /// </summary>
44 public const string GreaterThanOrEqualOperator =
45 "GREATER_THAN_OR_EQUAL";
46
47 /// <summary>
48 /// Identifies a strict less-than comparison.
49 /// </summary>
50 public const string LessThanOperator = "LESS_THAN";
51
52 /// <summary>
53 /// Identifies a less-than-or-equal comparison.
54 /// </summary>
55 public const string LessThanOrEqualOperator =
56 "LESS_THAN_OR_EQUAL";
57
58 /// <summary>
59 /// Identifies a collection or text containment test.
60 /// </summary>
61 public const string ContainsOperator = "CONTAINS";
62
63 /// <summary>
64 /// Identifies a negative collection or text
65 /// containment test.
66 /// </summary>
67 public const string NotContainsOperator = "NOT_CONTAINS";
68
69 /// <summary>
70 /// Identifies a membership test against a collection
71 /// of accepted values.
72 /// </summary>
73 public const string InOperator = "IN";
74
75 /// <summary>
76 /// Identifies a negative membership test.
77 /// </summary>
78 public const string NotInOperator = "NOT_IN";
79
80 /// <summary>
81 /// Identifies a Boolean condition requiring the
82 /// observed value to be true.
83 /// </summary>
84 public const string IsTrueOperator = "IS_TRUE";
85
86 /// <summary>
87 /// Identifies a Boolean condition requiring the
88 /// observed value to be false.
89 /// </summary>
90 public const string IsFalseOperator = "IS_FALSE";
91
92 private static readonly HashSet<string>
93 SupportedOperatorCodes =
94 new(
95 new[]
96 {
109 },
110 StringComparer.OrdinalIgnoreCase);
111
112 private string _ruleCode =
113 string.Empty;
114
115 private string _featureCode =
116 string.Empty;
117
118 private string _operatorCode =
119 string.Empty;
120
121 private string _expectedValue =
122 string.Empty;
123
124 private string _description =
125 string.Empty;
126
127 private double _weight = 1.0;
128 private bool _isEnabled = true;
129
130 /// <summary>
131 /// Initializes an empty known-problem rule definition.
132 /// </summary>
133 /// <remarks>
134 /// This constructor is required for XML serialization.
135 /// </remarks>
137 {
138 }
139
140 /// <summary>
141 /// Initializes a known-problem rule definition.
142 /// </summary>
143 /// <param name="ruleCode">
144 /// Stable code identifying the classification rule.
145 /// </param>
146 /// <param name="featureCode">
147 /// Code of the feature evaluated by the rule.
148 /// </param>
149 /// <param name="operatorCode">
150 /// Code of the comparison operator.
151 /// </param>
152 /// <param name="expectedValue">
153 /// Optional expected value used by the comparison.
154 /// </param>
155 /// <param name="description">
156 /// Optional human-readable description of the rule.
157 /// </param>
158 /// <exception cref="ArgumentException">
159 /// Thrown when <paramref name="ruleCode"/>,
160 /// <paramref name="featureCode"/> or
161 /// <paramref name="operatorCode"/> is empty.
162 /// </exception>
164 string ruleCode,
165 string featureCode,
166 string operatorCode,
167 string expectedValue = "",
168 string description = "")
169 {
170 if (string.IsNullOrWhiteSpace(ruleCode))
171 {
172 throw new ArgumentException(
173 "A classification-rule code is required.",
174 nameof(ruleCode));
175 }
176
177 if (string.IsNullOrWhiteSpace(featureCode))
178 {
179 throw new ArgumentException(
180 "A feature code is required.",
181 nameof(featureCode));
182 }
183
184 if (string.IsNullOrWhiteSpace(operatorCode))
185 {
186 throw new ArgumentException(
187 "A comparison-operator code is required.",
188 nameof(operatorCode));
189 }
190
191 RuleCode = ruleCode;
192 FeatureCode = featureCode;
193 OperatorCode = operatorCode;
194 ExpectedValue = expectedValue;
195 Description = description;
196 }
197
198 /// <summary>
199 /// Gets or sets the stable code identifying this
200 /// classification rule.
201 /// </summary>
202 /// <remarks>
203 /// Examples include <c>LSU.SINGLE_ITEM</c>,
204 /// <c>LSU.UNCAPACITATED</c> and
205 /// <c>EXT.TRANSPORTATION</c>.
206 /// </remarks>
207 [XmlAttribute("ruleCode")]
208 public string RuleCode
209 {
210 get => _ruleCode;
211 set
212 {
213 if (SetProperty(
214 ref _ruleCode,
215 NormalizeRuleCode(value)))
216 {
217 NotifyDefinitionProperties();
218 }
219 }
220 }
221
222 /// <summary>
223 /// Gets or sets the stable code of the feature evaluated
224 /// by this rule.
225 /// </summary>
226 /// <remarks>
227 /// The feature code normally identifies a public property
228 /// of <see cref="LotSizingProblemFeatures"/>.
229 ///
230 /// Feature lookup should be case-insensitive.
231 ///
232 /// Examples include <c>itemCount</c>,
233 /// <c>isMultiLevel</c>, <c>isCapacitated</c> and
234 /// <c>hasTransportation</c>.
235 /// </remarks>
236 [XmlAttribute("featureCode")]
237 public string FeatureCode
238 {
239 get => _featureCode;
240 set
241 {
242 if (SetProperty(
243 ref _featureCode,
244 value?.Trim() ?? string.Empty))
245 {
246 NotifyDefinitionProperties();
247 }
248 }
249 }
250
251 /// <summary>
252 /// Gets or sets the code of the comparison operator used
253 /// by this rule.
254 /// </summary>
255 /// <remarks>
256 /// Supported operator codes are:
257 /// <list type="bullet">
258 /// <item><description><c>EQUALS</c>;</description></item>
259 /// <item><description><c>NOT_EQUALS</c>;</description></item>
260 /// <item><description><c>GREATER_THAN</c>;</description></item>
261 /// <item><description><c>GREATER_THAN_OR_EQUAL</c>;</description></item>
262 /// <item><description><c>LESS_THAN</c>;</description></item>
263 /// <item><description><c>LESS_THAN_OR_EQUAL</c>;</description></item>
264 /// <item><description><c>CONTAINS</c>;</description></item>
265 /// <item><description><c>NOT_CONTAINS</c>;</description></item>
266 /// <item><description><c>IN</c>;</description></item>
267 /// <item><description><c>NOT_IN</c>;</description></item>
268 /// <item><description><c>IS_TRUE</c>;</description></item>
269 /// <item><description><c>IS_FALSE</c>.</description></item>
270 /// </list>
271 /// </remarks>
272 [XmlAttribute("operatorCode")]
273 public string OperatorCode
274 {
275 get => _operatorCode;
276 set
277 {
278 if (SetProperty(
279 ref _operatorCode,
280 NormalizeOperatorCode(value)))
281 {
282 NotifyDefinitionProperties();
283 }
284 }
285 }
286
287 /// <summary>
288 /// Gets or sets the value expected by the comparison.
289 /// </summary>
290 /// <remarks>
291 /// The value is stored as invariant text so that the same
292 /// class can represent Boolean, numerical, enumeration and
293 /// textual comparisons.
294 ///
295 /// The <c>IS_TRUE</c> and <c>IS_FALSE</c> operators do not
296 /// require an expected value.
297 ///
298 /// For <c>IN</c> and <c>NOT_IN</c>, several values may be
299 /// separated by semicolons.
300 /// </remarks>
301 [XmlElement("expectedValue")]
302 public string ExpectedValue
303 {
304 get => _expectedValue;
305 set
306 {
307 if (SetProperty(
308 ref _expectedValue,
309 value?.Trim() ?? string.Empty))
310 {
311 NotifyDefinitionProperties();
312 }
313 }
314 }
315
316 /// <summary>
317 /// Gets or sets a human-readable description of the
318 /// classification rule.
319 /// </summary>
320 [XmlElement("description")]
321 public string Description
322 {
323 get => _description;
324 set => SetProperty(
325 ref _description,
326 value?.Trim() ?? string.Empty);
327 }
328
329 /// <summary>
330 /// Gets or sets the relative importance of this rule
331 /// during weighted matching.
332 /// </summary>
333 /// <remarks>
334 /// A weight of zero keeps the rule informative without
335 /// affecting the weighted matching score.
336 ///
337 /// Whether the rule is required, optional, an extension
338 /// or an exclusion is determined by the collection in
339 /// which its code appears in
340 /// <see cref="KnownProblemTypeDefinition"/>.
341 /// </remarks>
342 /// <exception cref="ArgumentOutOfRangeException">
343 /// Thrown when the supplied value is negative or not
344 /// finite.
345 /// </exception>
346 [XmlAttribute("weight")]
347 public double Weight
348 {
349 get => _weight;
350 set
351 {
352 if (!double.IsFinite(value) ||
353 value < 0.0)
354 {
355 throw new ArgumentOutOfRangeException(
356 nameof(value),
357 value,
358 "The rule weight must be finite and " +
359 "non-negative.");
360 }
361
362 SetProperty(
363 ref _weight,
364 value);
365 }
366 }
367
368 /// <summary>
369 /// Gets or sets a value indicating whether this rule is
370 /// enabled for automatic evaluation.
371 /// </summary>
372 [XmlAttribute("enabled")]
373 public bool IsEnabled
374 {
375 get => _isEnabled;
376 set
377 {
378 if (SetProperty(
379 ref _isEnabled,
380 value))
381 {
382 OnPropertyChanged(
383 nameof(CanBeEvaluated));
384 }
385 }
386 }
387
388 /// <summary>
389 /// Gets a value indicating whether a stable rule code
390 /// has been recorded.
391 /// </summary>
392 [XmlIgnore]
393 public bool HasRuleCode =>
394 !string.IsNullOrWhiteSpace(
395 RuleCode);
396
397 /// <summary>
398 /// Gets a value indicating whether a feature code has
399 /// been recorded.
400 /// </summary>
401 [XmlIgnore]
402 public bool HasFeatureCode =>
403 !string.IsNullOrWhiteSpace(
405
406 /// <summary>
407 /// Gets a value indicating whether the operator code is
408 /// supported by the standard rule evaluator.
409 /// </summary>
410 [XmlIgnore]
411 public bool HasSupportedOperator =>
414
415 /// <summary>
416 /// Gets a value indicating whether this operator requires
417 /// an explicit expected value.
418 /// </summary>
419 [XmlIgnore]
421 !string.Equals(
424 StringComparison.OrdinalIgnoreCase) &&
425 !string.Equals(
428 StringComparison.OrdinalIgnoreCase);
429
430 /// <summary>
431 /// Gets a value indicating whether an expected value has
432 /// been recorded.
433 /// </summary>
434 [XmlIgnore]
435 public bool HasExpectedValue =>
436 !string.IsNullOrWhiteSpace(
438
439 /// <summary>
440 /// Gets a value indicating whether this rule contains the
441 /// metadata required for evaluation.
442 /// </summary>
443 [XmlIgnore]
444 public bool IsValidDefinition =>
445 HasRuleCode &&
448 (
451 );
452
453 /// <summary>
454 /// Gets a value indicating whether this rule can
455 /// currently be evaluated.
456 /// </summary>
457 [XmlIgnore]
458 public bool CanBeEvaluated =>
459 IsEnabled &&
461
462 /// <summary>
463 /// Determines whether an operator code is supported by
464 /// the standard rule evaluator.
465 /// </summary>
466 /// <param name="operatorCode">
467 /// Operator code to examine.
468 /// </param>
469 /// <returns>
470 /// <see langword="true"/> when the code identifies one of
471 /// the standard comparison operators.
472 /// </returns>
473 public static bool IsSupportedOperatorCode(
474 string operatorCode)
475 {
476 string normalizedOperatorCode =
477 NormalizeOperatorCode(
478 operatorCode);
479
480 return SupportedOperatorCodes.Contains(
481 normalizedOperatorCode);
482 }
483
484 /// <summary>
485 /// Returns all standard comparison-operator codes.
486 /// </summary>
487 /// <returns>
488 /// Ordered collection of supported operator codes.
489 /// </returns>
490 public static IReadOnlyList<string>
492 {
493 return SupportedOperatorCodes
494 .OrderBy(
495 operatorCode =>
496 operatorCode,
497 StringComparer.Ordinal)
498 .ToArray();
499 }
500
501 /// <inheritdoc/>
502 public override string ToString()
503 {
504 string expectedDescription =
506 ? $" {ExpectedValue}"
507 : string.Empty;
508
509 return
510 $"{RuleCode}: {FeatureCode} " +
511 $"{OperatorCode}" +
512 $"{expectedDescription}";
513 }
514
515 private static string NormalizeRuleCode(
516 string? value)
517 {
518 return value?
519 .Trim()
520 .ToUpperInvariant() ??
521 string.Empty;
522 }
523
524 private static string NormalizeOperatorCode(
525 string? value)
526 {
527 if (string.IsNullOrWhiteSpace(value))
528 {
529 return string.Empty;
530 }
531
532 string compactCode =
533 new(
534 value
535 .Where(
536 character =>
537 char.IsLetterOrDigit(
538 character))
539 .Select(
540 character =>
541 char.ToUpperInvariant(
542 character))
543 .ToArray());
544
545 return compactCode switch
546 {
547 "EQUALS" or "EQUAL" =>
549
550 "NOTEQUALS" or "NOTEQUAL" =>
552
553 "GREATERTHAN" =>
555
556 "GREATERTHANOREQUAL" or
557 "GREATERTHANOREQUALTO" =>
559
560 "LESSTHAN" =>
562
563 "LESSTHANOREQUAL" or
564 "LESSTHANOREQUALTO" =>
566
567 "CONTAINS" or "CONTAIN" =>
569
570 "NOTCONTAINS" or "NOTCONTAIN" =>
572
573 "IN" =>
575
576 "NOTIN" =>
578
579 "ISTRUE" or "TRUE" =>
581
582 "ISFALSE" or "FALSE" =>
584
585 _ =>
586 value.Trim().ToUpperInvariant()
587 };
588 }
589
590 private void NotifyDefinitionProperties()
591 {
592 OnPropertyChanged(
593 nameof(HasRuleCode));
594
595 OnPropertyChanged(
596 nameof(HasFeatureCode));
597
598 OnPropertyChanged(
599 nameof(HasSupportedOperator));
600
601 OnPropertyChanged(
602 nameof(RequiresExpectedValue));
603
604 OnPropertyChanged(
605 nameof(HasExpectedValue));
606
607 OnPropertyChanged(
608 nameof(IsValidDefinition));
609
610 OnPropertyChanged(
611 nameof(CanBeEvaluated));
612 }
613}
const string GreaterThanOperator
Identifies a strict greater-than comparison.
string ExpectedValue
Gets or sets the value expected by the comparison.
string FeatureCode
Gets or sets the stable code of the feature evaluated by this rule.
const string NotContainsOperator
Identifies a negative collection or text containment test.
bool HasRuleCode
Gets a value indicating whether a stable rule code has been recorded.
bool HasFeatureCode
Gets a value indicating whether a feature code has been recorded.
const string ContainsOperator
Identifies a collection or text containment test.
const string GreaterThanOrEqualOperator
Identifies a greater-than-or-equal comparison.
const string IsFalseOperator
Identifies a Boolean condition requiring the observed value to be false.
string RuleCode
Gets or sets the stable code identifying this classification rule.
const string LessThanOperator
Identifies a strict less-than comparison.
const string InOperator
Identifies a membership test against a collection of accepted values.
KnownProblemRuleDefinition(string ruleCode, string featureCode, string operatorCode, string expectedValue="", string description="")
Initializes a known-problem rule definition.
const string IsTrueOperator
Identifies a Boolean condition requiring the observed value to be true.
bool CanBeEvaluated
Gets a value indicating whether this rule can currently be evaluated.
static bool IsSupportedOperatorCode(string operatorCode)
Determines whether an operator code is supported by the standard rule evaluator.
string Description
Gets or sets a human-readable description of the classification rule.
bool RequiresExpectedValue
Gets a value indicating whether this operator requires an explicit expected value.
const string LessThanOrEqualOperator
Identifies a less-than-or-equal comparison.
static IReadOnlyList< string > GetSupportedOperatorCodes()
Returns all standard comparison-operator codes.
double Weight
Gets or sets the relative importance of this rule during weighted matching.
bool HasSupportedOperator
Gets a value indicating whether the operator code is supported by the standard rule evaluator.
bool HasExpectedValue
Gets a value indicating whether an expected value has been recorded.
bool IsEnabled
Gets or sets a value indicating whether this rule is enabled for automatic evaluation.
string OperatorCode
Gets or sets the code of the comparison operator used by this rule.
KnownProblemRuleDefinition()
Initializes an empty known-problem rule definition.
bool IsValidDefinition
Gets a value indicating whether this rule contains the metadata required for evaluation.