Skip to content

Fixes #5352 issues caused by equality with non-string values for root cause localization #5354

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Aug 25, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions src/Microsoft.ML.TimeSeries/RootCauseAnalyzer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ private DimensionInfo SeparateDimension(Dictionary<string, Object> dimensions, O
foreach (KeyValuePair<string, Object> entry in dimensions)
{
string key = entry.Key;
if (aggSymbol.Equals(entry.Value))
if (object.Equals(aggSymbol, entry.Value))
{
info.AggDims.Add(key);
}
Expand Down Expand Up @@ -723,7 +723,7 @@ private static bool ContainsAll(Dictionary<string, Object> bigDictionary, Dictio
{
foreach (var item in smallDictionary)
{
if (!bigDictionary.ContainsKey(item.Key) || !bigDictionary[item.Key].Equals(smallDictionary[item.Key]))
if (!bigDictionary.ContainsKey(item.Key) || !object.Equals(bigDictionary[item.Key], smallDictionary[item.Key]))
{
return false;
}
Expand All @@ -734,7 +734,7 @@ private static bool ContainsAll(Dictionary<string, Object> bigDictionary, Dictio

private bool IsAggregationDimension(Object val, Object aggSymbol)
{
return Convert.ToString(val).Equals(aggSymbol);
return object.Equals(val, aggSymbol);
}
}

Expand Down Expand Up @@ -827,7 +827,7 @@ public override bool Equals(Dictionary<string, object> x, Dictionary<string, obj
}
foreach (var pair in x)
{
if (!pair.Value.Equals(y[pair.Key]))
if (!object.Equals(pair.Value, y[pair.Key]))
{
return false;
}
Expand Down
4 changes: 2 additions & 2 deletions src/Microsoft.ML.TimeSeries/RootCauseLocalizationType.cs
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ public bool Equals(RootCauseItem other)
{
foreach (KeyValuePair<string, Object> item in Dimension)
{
if (!other.Dimension[item.Key].Equals(item.Value))
if (!object.Equals(other.Dimension[item.Key], item.Value))
{
return false;
}
Expand Down Expand Up @@ -228,7 +228,7 @@ public bool Equals(TimeSeriesPoint other)
{
foreach (KeyValuePair<string, Object> item in Dimension)
{
if (!other.Dimension[item.Key].Equals(item.Value))
if (!object.Equals(other.Dimension[item.Key], item.Value))
{
return false;
}
Expand Down
201 changes: 183 additions & 18 deletions test/Microsoft.ML.TimeSeries.Tests/TimeSeriesDirectApi.cs
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,8 @@ private sealed class SrCnnAnomalyDetection
}

private static Object _rootCauseAggSymbol = "##SUM##";
private static int _rootCauseAggSymbolForIntDimValue = 0;
private static string _rootCauseAggSymbolForDiffDimValueType = "0";


[Fact]
Expand Down Expand Up @@ -757,7 +759,7 @@ public void TestSrCnnAnomalyDetectorWithSeasonalAnomalyData(
public void RootCauseLocalization()
{
// Create an root cause localizatiom input
var rootCauseLocalizationInput = new RootCauseLocalizationInput(GetRootCauseTimestamp(), GetRootCauseAnomalyDimension(), new List<MetricSlice>() { new MetricSlice(GetRootCauseTimestamp(), GetRootCauseLocalizationPoints()) }, AggregateType.Sum, _rootCauseAggSymbol);
var rootCauseLocalizationInput = new RootCauseLocalizationInput(GetRootCauseTimestamp(), GetRootCauseAnomalyDimension("UK", _rootCauseAggSymbol), new List<MetricSlice>() { new MetricSlice(GetRootCauseTimestamp(), GetRootCauseLocalizationPoints(_rootCauseAggSymbol)) }, AggregateType.Sum, _rootCauseAggSymbol);

var ml = new MLContext(1);
RootCause rootCause = ml.AnomalyDetection.LocalizeRootCause(rootCauseLocalizationInput);
Expand All @@ -784,7 +786,7 @@ public void RootCauseLocalization()
public void MultiDimensionalRootCauseLocalization()
{
// Create an root cause localizatiom input
var rootCauseLocalizationInput = new RootCauseLocalizationInput(GetRootCauseTimestamp(), GetRootCauseAnomalyDimension(), new List<MetricSlice>() { new MetricSlice(GetRootCauseTimestamp(), GetRootCauseLocalizationPoints()) }, AggregateType.Sum, _rootCauseAggSymbol);
var rootCauseLocalizationInput = new RootCauseLocalizationInput(GetRootCauseTimestamp(), GetRootCauseAnomalyDimension("UK", _rootCauseAggSymbol), new List<MetricSlice>() { new MetricSlice(GetRootCauseTimestamp(), GetRootCauseLocalizationPoints(_rootCauseAggSymbol)) }, AggregateType.Sum, _rootCauseAggSymbol);

var ml = new MLContext(1);
List<RootCause> preparedCauses = ml.AnomalyDetection.LocalizeRootCauses(rootCauseLocalizationInput);
Expand Down Expand Up @@ -816,7 +818,7 @@ public void MultiDimensionalRootCauseLocalization()

expectedDim = new Dictionary<string, Object>();
expectedDim.Add("Country", "UK");
expectedDim.Add("DeviceType", "Laptop");
expectedDim.Add("DeviceType", "Mobile");
expectedDim.Add("DataCenter", _rootCauseAggSymbol);

foreach (KeyValuePair<string, object> pair in preparedCauses[1].Items[0].Dimension)
Expand All @@ -825,6 +827,40 @@ public void MultiDimensionalRootCauseLocalization()
}
}

[Fact]
public void RootCauseLocalizationForNullDimValue()
{
// Create an root cause localizatiom input
object rootCauseAggSymbolForNullDimValue = null;
List<MetricSlice> slice = new List<MetricSlice>
{
new MetricSlice(GetRootCauseTimestamp(), GetRootCauseLocalizationPoints(rootCauseAggSymbolForNullDimValue))
};
var rootCauseLocalizationInput = new RootCauseLocalizationInput(GetRootCauseTimestamp(), GetRootCauseAnomalyDimension("UK", rootCauseAggSymbolForNullDimValue), slice, AggregateType.Sum, rootCauseAggSymbolForNullDimValue);

var ml = new MLContext(1);
RootCause rootCause = ml.AnomalyDetection.LocalizeRootCause(rootCauseLocalizationInput);

Assert.NotNull(rootCause);
Assert.Single(rootCause.Items);
Assert.Equal(3, rootCause.Items[0].Dimension.Count);
Assert.Equal(AnomalyDirection.Up, rootCause.Items[0].Direction);
Assert.Single(rootCause.Items[0].Path);
Assert.Equal("DataCenter", rootCause.Items[0].Path[0]);

Dictionary<string, object> expectedDim = new Dictionary<string, object>
{
{"Country", "UK" },
{"DeviceType", rootCauseAggSymbolForNullDimValue },
{"DataCenter", "DC1" }
};

foreach (KeyValuePair<string, object> pair in rootCause.Items[0].Dimension)
{
Assert.Equal(expectedDim[pair.Key], pair.Value);
}
}

[Theory]
[InlineData(-1, 6)]
[InlineData(60, 6)]
Expand All @@ -848,7 +884,7 @@ public void TestDetectSeasonality(int seasonalityWindowSize, int expectedPeriod)
Assert.Equal(expectedPeriod, period);
}

private static List<TimeSeriesPoint> GetRootCauseLocalizationPoints()
private static List<TimeSeriesPoint> GetRootCauseLocalizationPoints(object aggSymbol)
{
List<TimeSeriesPoint> points = new List<TimeSeriesPoint>();

Expand All @@ -866,7 +902,7 @@ private static List<TimeSeriesPoint> GetRootCauseLocalizationPoints()

Dictionary<string, Object> dic3 = new Dictionary<string, Object>();
dic3.Add("Country", "UK");
dic3.Add("DeviceType", _rootCauseAggSymbol);
dic3.Add("DeviceType", aggSymbol);
dic3.Add("DataCenter", "DC1");
points.Add(new TimeSeriesPoint(1200, 200, true, dic3));

Expand All @@ -884,27 +920,27 @@ private static List<TimeSeriesPoint> GetRootCauseLocalizationPoints()

Dictionary<string, Object> dic6 = new Dictionary<string, Object>();
dic6.Add("Country", "UK");
dic6.Add("DeviceType", _rootCauseAggSymbol);
dic6.Add("DeviceType", aggSymbol);
dic6.Add("DataCenter", "DC2");
points.Add(new TimeSeriesPoint(300, 300, false, dic6));

Dictionary<string, Object> dic7 = new Dictionary<string, Object>();
dic7.Add("Country", "UK");
dic7.Add("DeviceType", _rootCauseAggSymbol);
dic7.Add("DataCenter", _rootCauseAggSymbol);
points.Add(new TimeSeriesPoint(1800, 750, true, dic7));
dic7.Add("DeviceType", aggSymbol);
dic7.Add("DataCenter", aggSymbol);
points.Add(new TimeSeriesPoint(1800, 850, true, dic7));

Dictionary<string, Object> dic8 = new Dictionary<string, Object>();
dic8.Add("Country", "UK");
dic8.Add("DeviceType", "Laptop");
dic8.Add("DataCenter", _rootCauseAggSymbol);
points.Add(new TimeSeriesPoint(1500, 450, true, dic8));
dic8.Add("DataCenter", aggSymbol);
points.Add(new TimeSeriesPoint(500, 450, false, dic8));

Dictionary<string, Object> dic9 = new Dictionary<string, Object>();
dic9.Add("Country", "UK");
dic9.Add("DeviceType", "Mobile");
dic9.Add("DataCenter", _rootCauseAggSymbol);
points.Add(new TimeSeriesPoint(600, 550, false, dic9));
dic9.Add("DataCenter", aggSymbol);
points.Add(new TimeSeriesPoint(1300, 400, true, dic9));

Dictionary<string, Object> dic10 = new Dictionary<string, Object>();
dic10.Add("Country", "UK");
Expand All @@ -920,19 +956,19 @@ private static List<TimeSeriesPoint> GetRootCauseLocalizationPoints()

Dictionary<string, Object> dic12 = new Dictionary<string, Object>();
dic12.Add("Country", "UK");
dic12.Add("DeviceType", _rootCauseAggSymbol);
dic12.Add("DeviceType", aggSymbol);
dic12.Add("DataCenter", "DC3");
points.Add(new TimeSeriesPoint(300, 350, false, dic12));

return points;
}

private static Dictionary<string, Object> GetRootCauseAnomalyDimension()
private static Dictionary<string, Object> GetRootCauseAnomalyDimension(object val, object aggSymbol)
{
Dictionary<string, Object> dim = new Dictionary<string, Object>();
dim.Add("Country", "UK");
dim.Add("DeviceType", _rootCauseAggSymbol);
dim.Add("DataCenter", _rootCauseAggSymbol);
dim.Add("Country", val);
dim.Add("DeviceType", aggSymbol);
dim.Add("DataCenter", aggSymbol);

return dim;
}
Expand All @@ -941,5 +977,134 @@ private static DateTime GetRootCauseTimestamp()
{
return new DateTime(2020, 3, 23, 0, 0, 0);
}

[Fact]
public void RootCauseLocalizationForIntDimValue()
{
// Create an root cause localizatiom input
List<MetricSlice> slice = new List<MetricSlice>
{
new MetricSlice(GetRootCauseTimestamp(), GetRootCauseLocalizationPointsForIntDimValue())
};
var rootCauseLocalizationInput = new RootCauseLocalizationInput(GetRootCauseTimestamp(), GetRootCauseAnomalyDimension(10, _rootCauseAggSymbolForIntDimValue), slice, AggregateType.Sum, _rootCauseAggSymbolForIntDimValue);

var ml = new MLContext(1);
RootCause rootCause = ml.AnomalyDetection.LocalizeRootCause(rootCauseLocalizationInput);

Assert.NotNull(rootCause);
Assert.Single(rootCause.Items);
Assert.Equal(3, rootCause.Items[0].Dimension.Count);
Assert.Equal(AnomalyDirection.Up, rootCause.Items[0].Direction);
Assert.Single(rootCause.Items[0].Path);
Assert.Equal("DataCenter", rootCause.Items[0].Path[0]);

Dictionary<string, int> expectedDim = new Dictionary<string, int>
{
{"Country", 10 },
{"DeviceType", _rootCauseAggSymbolForIntDimValue },
{"DataCenter", 30 }
};

foreach (KeyValuePair<string, object> pair in rootCause.Items[0].Dimension)
{
Assert.Equal(expectedDim[pair.Key], pair.Value);
}
}

[Fact]
public void RootCauseLocalizationForDiffDimValueType()
{
// Create an root cause localizatiom input
Dictionary<string, object> expectedDim = GetRootCauseAnomalyDimension(10, _rootCauseAggSymbolForIntDimValue);
List<MetricSlice> slice = new List<MetricSlice>
{
new MetricSlice(GetRootCauseTimestamp(), GetRootCauseLocalizationPointsForIntDimValue())
};
var rootCauseLocalizationInput = new RootCauseLocalizationInput(GetRootCauseTimestamp(), expectedDim, slice, AggregateType.Sum, _rootCauseAggSymbolForDiffDimValueType);

var ml = new MLContext(1);
RootCause rootCause = ml.AnomalyDetection.LocalizeRootCause(rootCauseLocalizationInput);

Assert.Null(rootCause);
}

private static List<TimeSeriesPoint> GetRootCauseLocalizationPointsForIntDimValue()
{
List<TimeSeriesPoint> points = new List<TimeSeriesPoint>();

Dictionary<string, object> dic1 = new Dictionary<string, object>();
dic1.Add("Country", 10);
dic1.Add("DeviceType", 20);
dic1.Add("DataCenter", 30);
points.Add(new TimeSeriesPoint(200, 100, true, dic1));

Dictionary<string, object> dic2 = new Dictionary<string, object>();
dic2.Add("Country", 10);
dic2.Add("DeviceType", 21);
dic2.Add("DataCenter", 30);
points.Add(new TimeSeriesPoint(1000, 100, true, dic2));

Dictionary<string, object> dic3 = new Dictionary<string, object>();
dic3.Add("Country", 10);
dic3.Add("DeviceType", _rootCauseAggSymbolForIntDimValue);
dic3.Add("DataCenter", 30);
points.Add(new TimeSeriesPoint(1200, 200, true, dic3));

Dictionary<string, object> dic4 = new Dictionary<string, object>();
dic4.Add("Country", 10);
dic4.Add("DeviceType", 20);
dic4.Add("DataCenter", 31);
points.Add(new TimeSeriesPoint(100, 100, false, dic4));

Dictionary<string, object> dic5 = new Dictionary<string, object>();
dic5.Add("Country", 10);
dic5.Add("DeviceType", 21);
dic5.Add("DataCenter", 31);
points.Add(new TimeSeriesPoint(200, 200, false, dic5));

Dictionary<string, object> dic6 = new Dictionary<string, object>();
dic6.Add("Country", 10);
dic6.Add("DeviceType", _rootCauseAggSymbolForIntDimValue);
dic6.Add("DataCenter", 31);
points.Add(new TimeSeriesPoint(300, 300, false, dic6));

Dictionary<string, object> dic7 = new Dictionary<string, object>();
dic7.Add("Country", 10);
dic7.Add("DeviceType", _rootCauseAggSymbolForIntDimValue);
dic7.Add("DataCenter", _rootCauseAggSymbolForIntDimValue);
points.Add(new TimeSeriesPoint(1800, 850, true, dic7));

Dictionary<string, object> dic8 = new Dictionary<string, object>();
dic8.Add("Country", 10);
dic8.Add("DeviceType", 20);
dic8.Add("DataCenter", _rootCauseAggSymbolForIntDimValue);
points.Add(new TimeSeriesPoint(500, 450, false, dic8));

Dictionary<string, object> dic9 = new Dictionary<string, object>();
dic9.Add("Country", 10);
dic9.Add("DeviceType", 21);
dic9.Add("DataCenter", _rootCauseAggSymbolForIntDimValue);
points.Add(new TimeSeriesPoint(1300, 400, true, dic9));

Dictionary<string, object> dic10 = new Dictionary<string, object>();
dic10.Add("Country", 10);
dic10.Add("DeviceType", 21);
dic10.Add("DataCenter", 32);
points.Add(new TimeSeriesPoint(100, 100, false, dic10));

Dictionary<string, object> dic11 = new Dictionary<string, object>();
dic11.Add("Country", 10);
dic11.Add("DeviceType", 20);
dic11.Add("DataCenter", 32);
points.Add(new TimeSeriesPoint(200, 250, false, dic11));

Dictionary<string, object> dic12 = new Dictionary<string, object>();
dic12.Add("Country", 10);
dic12.Add("DeviceType", _rootCauseAggSymbolForIntDimValue);
dic12.Add("DataCenter", 32);
points.Add(new TimeSeriesPoint(300, 350, false, dic12));

return points;
}
}
}