private void AddNode(TreeNode tn, Explanation e) { TreeNode node = new TreeNode(e.GetValue().ToString("G4") + " " + e.GetDescription()); if (null == tn) { treeExplain.Nodes.Add(node); } else { tn.Nodes.Add(node); } Explanation[] kids = e.GetDetails(); if (kids != null && kids.Length > 0) { for (int i = 0; i < kids.Length; i++) { AddNode(node, kids[i]); } } }
/// <summary> /// Assert that an explanation has the expected score, and optionally that its /// sub-details max/sum/factor match to that score. /// </summary> /// <param name="q"> String representation of the query for assertion messages. </param> /// <param name="doc"> Document ID for assertion messages. </param> /// <param name="score"> Real score value of doc with query <paramref name="q"/>. </param> /// <param name="deep"> Indicates whether a deep comparison of sub-Explanation details should be executed. </param> /// <param name="expl"> The <see cref="Explanation"/> to match against score. </param> public static void VerifyExplanation(string q, int doc, float score, bool deep, Explanation expl) { float value = expl.Value; Assert.AreEqual(score, value, ExplainToleranceDelta(score, value), q + ": score(doc=" + doc + ")=" + score + " != explanationScore=" + value + " Explanation: " + expl); if (!deep) { return; } Explanation[] detail = expl.GetDetails(); // TODO: can we improve this entire method? its really geared to work only with TF/IDF if (expl.Description.EndsWith("computed from:", StringComparison.Ordinal)) { return; // something more complicated. } if (detail != null) { if (detail.Length == 1) { // simple containment, unless its a freq of: (which lets a query explain how the freq is calculated), // just verify contained expl has same score if (!expl.Description.EndsWith("with freq of:", StringComparison.Ordinal)) { VerifyExplanation(q, doc, score, deep, detail[0]); } } else { // explanation must either: // - end with one of: "product of:", "sum of:", "max of:", or // - have "max plus <x> times others" (where <x> is float). float x = 0; string descr = CultureInfo.InvariantCulture.TextInfo.ToLower(expl.Description); bool productOf = descr.EndsWith("product of:", StringComparison.Ordinal); bool sumOf = descr.EndsWith("sum of:", StringComparison.Ordinal); bool maxOf = descr.EndsWith("max of:", StringComparison.Ordinal); bool maxTimesOthers = false; if (!(productOf || sumOf || maxOf)) { // maybe 'max plus x times others' int k1 = descr.IndexOf("max plus ", StringComparison.Ordinal); if (k1 >= 0) { k1 += "max plus ".Length; int k2 = descr.IndexOf(" ", k1, StringComparison.Ordinal); try { // LUCENENET NOTE: Using current culture here is intentional because // we are parsing from text that was made using the current culture. x = Convert.ToSingle(descr.Substring(k1, k2 - k1).Trim()); if (descr.Substring(k2).Trim().Equals("times others of:", StringComparison.Ordinal)) { maxTimesOthers = true; } } #pragma warning disable 168 catch (FormatException e) #pragma warning restore 168 { } } } // TODO: this is a TERRIBLE assertion!!!! Assert.IsTrue(productOf || sumOf || maxOf || maxTimesOthers, q + ": multi valued explanation description=\"" + descr + "\" must be 'max of plus x times others' or end with 'product of'" + " or 'sum of:' or 'max of:' - " + expl); float sum = 0; float product = 1; float max = 0; for (int i = 0; i < detail.Length; i++) { float dval = detail[i].Value; VerifyExplanation(q, doc, dval, deep, detail[i]); product *= dval; sum += dval; max = Math.Max(max, dval); } float combined = 0; if (productOf) { combined = product; } else if (sumOf) { combined = sum; } else if (maxOf) { combined = max; } else if (maxTimesOthers) { combined = max + x * (sum - max); } else { Assert.IsTrue(false, "should never get here!"); } Assert.AreEqual(combined, value, ExplainToleranceDelta(combined, value), q + ": actual subDetails combined==" + combined + " != value=" + value + " Explanation: " + expl); } } }
/// <summary> Assert that an explanation has the expected score, and optionally that its /// sub-details max/sum/factor match to that score. /// /// </summary> /// <param name="q">String representation of the query for assertion messages /// </param> /// <param name="doc">Document ID for assertion messages /// </param> /// <param name="score">Real score value of doc with query q /// </param> /// <param name="deep">indicates whether a deep comparison of sub-Explanation details should be executed /// </param> /// <param name="expl">The Explanation to match against score /// </param> public static void VerifyExplanation(System.String q, int doc, float score, bool deep, Explanation expl) { float value_Renamed = expl.GetValue(); Assert.AreEqual(score, value_Renamed, EXPLAIN_SCORE_TOLERANCE_DELTA, q + ": score(doc=" + doc + ")=" + score + " != explanationScore=" + value_Renamed + " Explanation: " + expl); if (!deep) { return; } Explanation[] detail = expl.GetDetails(); if (detail != null) { if (detail.Length == 1) { // simple containment, no matter what the description says, // just verify contained expl has same score VerifyExplanation(q, doc, score, deep, detail[0]); } else { // explanation must either: // - end with one of: "product of:", "sum of:", "max of:", or // - have "max plus <x> times others" (where <x> is float). float x = 0; System.String descr = expl.GetDescription().ToLower(); bool productOf = descr.EndsWith("product of:"); bool sumOf = descr.EndsWith("sum of:"); bool maxOf = descr.EndsWith("max of:"); bool maxTimesOthers = false; if (!(productOf || sumOf || maxOf)) { // maybe 'max plus x times others' int k1 = descr.IndexOf("max plus "); if (k1 >= 0) { k1 += "max plus ".Length; int k2 = descr.IndexOf(" ", k1); try { x = SupportClass.Single.Parse(descr.Substring(k1, (k2) - (k1)).Trim()); if (descr.Substring(k2).Trim().Equals("times others of:")) { maxTimesOthers = true; } } catch (System.FormatException e) { } } } Assert.IsTrue(productOf || sumOf || maxOf || maxTimesOthers, q + ": multi valued explanation description=\"" + descr + "\" must be 'max of plus x times others' or end with 'product of'" + " or 'sum of:' or 'max of:' - " + expl); float sum = 0; float product = 1; float max = 0; for (int i = 0; i < detail.Length; i++) { float dval = detail[i].GetValue(); VerifyExplanation(q, doc, dval, deep, detail[i]); product *= dval; sum += dval; max = System.Math.Max(max, dval); } float combined = 0; if (productOf) { combined = product; } else if (sumOf) { combined = sum; } else if (maxOf) { combined = max; } else if (maxTimesOthers) { combined = max + x * (sum - max); } else { Assert.IsTrue(false, "should never get here!"); } Assert.AreEqual(combined, value_Renamed, EXPLAIN_SCORE_TOLERANCE_DELTA, q + ": actual subDetails combined==" + combined + " != value=" + value_Renamed + " Explanation: " + expl); } } }
public virtual Explanation Explain(IndexReader reader, int doc) { Explanation sumExpl = new Explanation(); sumExpl.SetDescription("sum of:"); int coord = 0; int maxCoord = 0; float sum = 0.0f; for (int i = 0; i < weights.Count; i++) { BooleanClause c = (BooleanClause)Enclosing_Instance.clauses[i]; Weight w = (Weight)weights[i]; Explanation e = w.Explain(reader, doc); if (!c.IsProhibited()) { maxCoord++; } if (e.GetValue() > 0) { if (!c.IsProhibited()) { sumExpl.AddDetail(e); sum += e.GetValue(); coord++; } else { return(new Explanation(0.0f, "match prohibited")); } } else if (c.IsRequired()) { return(new Explanation(0.0f, "match required")); } } sumExpl.SetValue(sum); if (coord == 1) { // only one clause matched sumExpl = sumExpl.GetDetails()[0]; // eliminate wrapper } float coordFactor = similarity.Coord(coord, maxCoord); if (coordFactor == 1.0f) { // coord is no-op return(sumExpl); } // eliminate wrapper else { Explanation result = new Explanation(); result.SetDescription("product of:"); result.AddDetail(sumExpl); result.AddDetail(new Explanation(coordFactor, "coord(" + coord + "/" + maxCoord + ")")); result.SetValue(sum * coordFactor); return(result); } }
/* * Assert that an explanation has the expected score, and optionally that its * sub-details max/sum/factor match to that score. * * @param q String representation of the query for assertion messages * @param doc Document ID for assertion messages * @param score Real score value of doc with query q * @param deep indicates whether a deep comparison of sub-Explanation details should be executed * @param expl The Explanation to match against score */ public static void verifyExplanation(String q, int doc, float score, bool deep, Explanation expl) { float value = expl.Value; assertEquals(q + ": score(doc=" + doc + ")=" + score + " != explanationScore=" + value + " Explanation: " + expl, score, value, explainToleranceDelta(score, value)); if (!deep) return; var detail = expl.GetDetails(); // TODO: can we improve this entire method? its really geared to work only with TF/IDF if (expl.Description.EndsWith("computed from:")) { return; // something more complicated. } if (detail != null) { if (detail.Length == 1) { // simple containment, unless its a freq of: (which lets a query explain how the freq is calculated), // just verify contained expl has same score if (!expl.Description.EndsWith("with freq of:")) verifyExplanation(q, doc, score, deep, detail[0]); } else { // explanation must either: // - end with one of: "product of:", "sum of:", "max of:", or // - have "max plus <x> times others" (where <x> is float). float x = 0; String descr = expl.Description.ToLowerInvariant(); bool productOf = descr.EndsWith("product of:"); bool sumOf = descr.EndsWith("sum of:"); bool maxOf = descr.EndsWith("max of:"); bool maxTimesOthers = false; if (!(productOf || sumOf || maxOf)) { // maybe 'max plus x times others' int k1 = descr.IndexOf("max plus "); if (k1 >= 0) { k1 += "max plus ".Length; int k2 = descr.IndexOf(" ", k1); try { x = float.Parse(descr.Substring(k1, k2).Trim()); if (descr.Substring(k2).Trim().Equals("times others of:")) { maxTimesOthers = true; } } catch (FormatException e) { } } } // TODO: this is a TERRIBLE assertion!!!! assertTrue( q + ": multi valued explanation description=\"" + descr + "\" must be 'max of plus x times others' or end with 'product of'" + " or 'sum of:' or 'max of:' - " + expl, productOf || sumOf || maxOf || maxTimesOthers); float sum = 0; float product = 1; float max = 0; for (int i = 0; i < detail.Length; i++) { float dval = detail[i].Value; verifyExplanation(q, doc, dval, deep, detail[i]); product *= dval; sum += dval; max = Math.Max(max, dval); } float combined = 0; if (productOf) { combined = product; } else if (sumOf) { combined = sum; } else if (maxOf) { combined = max; } else if (maxTimesOthers) { combined = max + x * (sum - max); } else { assertTrue("should never get here!", false); } assertEquals(q + ": actual subDetails combined==" + combined + " != value=" + value + " Explanation: " + expl, combined, value, explainToleranceDelta(combined, value)); } } }
/// <summary> Assert that an explanation has the expected score, and optionally that its /// sub-details max/sum/factor match to that score. /// /// </summary> /// <param name="q">String representation of the query for assertion messages /// </param> /// <param name="doc">Document ID for assertion messages /// </param> /// <param name="score">Real score value of doc with query q /// </param> /// <param name="deep">indicates whether a deep comparison of sub-Explanation details should be executed /// </param> /// <param name="expl">The Explanation to match against score /// </param> public static void VerifyExplanation(System.String q, int doc, float score, bool deep, Explanation expl) { float value_Renamed = expl.GetValue(); Assert.AreEqual(score, value_Renamed, EXPLAIN_SCORE_TOLERANCE_DELTA, q + ": score(doc=" + doc + ")=" + score + " != explanationScore=" + value_Renamed + " Explanation: " + expl); if (!deep) return ; Explanation[] detail = expl.GetDetails(); if (detail != null) { if (detail.Length == 1) { // simple containment, no matter what the description says, // just verify contained expl has same score VerifyExplanation(q, doc, score, deep, detail[0]); } else { // explanation must either: // - end with one of: "product of:", "sum of:", "max of:", or // - have "max plus <x> times others" (where <x> is float). float x = 0; System.String descr = expl.GetDescription().ToLower(); bool productOf = descr.EndsWith("product of:"); bool sumOf = descr.EndsWith("sum of:"); bool maxOf = descr.EndsWith("max of:"); bool maxTimesOthers = false; if (!(productOf || sumOf || maxOf)) { // maybe 'max plus x times others' int k1 = descr.IndexOf("max plus "); if (k1 >= 0) { k1 += "max plus ".Length; int k2 = descr.IndexOf(" ", k1); try { x = SupportClass.Single.Parse(descr.Substring(k1, (k2) - (k1)).Trim()); if (descr.Substring(k2).Trim().Equals("times others of:")) { maxTimesOthers = true; } } catch (System.FormatException) { } } } Assert.IsTrue(productOf || sumOf || maxOf || maxTimesOthers, q + ": multi valued explanation description=\"" + descr + "\" must be 'max of plus x times others' or end with 'product of'" + " or 'sum of:' or 'max of:' - " + expl); float sum = 0; float product = 1; float max = 0; for (int i = 0; i < detail.Length; i++) { float dval = detail[i].GetValue(); VerifyExplanation(q, doc, dval, deep, detail[i]); product *= dval; sum += dval; max = System.Math.Max(max, dval); } float combined = 0; if (productOf) { combined = product; } else if (sumOf) { combined = sum; } else if (maxOf) { combined = max; } else if (maxTimesOthers) { combined = max + x * (sum - max); } else { Assert.IsTrue(false, "should never get here!"); } Assert.AreEqual(combined, value_Renamed, EXPLAIN_SCORE_TOLERANCE_DELTA, q + ": actual subDetails combined==" + combined + " != value=" + value_Renamed + " Explanation: " + expl); } } }
public virtual Explanation Explain(IndexReader reader, int doc) { Explanation sumExpl = new Explanation(); sumExpl.SetDescription("sum of:"); int coord = 0; int maxCoord = 0; float sum = 0.0f; for (int i = 0; i < weights.Count; i++) { BooleanClause c = (BooleanClause) Enclosing_Instance.clauses[i]; Weight w = (Weight) weights[i]; Explanation e = w.Explain(reader, doc); if (!c.IsProhibited()) maxCoord++; if (e.GetValue() > 0) { if (!c.IsProhibited()) { sumExpl.AddDetail(e); sum += e.GetValue(); coord++; } else { return new Explanation(0.0f, "match prohibited"); } } else if (c.IsRequired()) { return new Explanation(0.0f, "match required"); } } sumExpl.SetValue(sum); if (coord == 1) // only one clause matched sumExpl = sumExpl.GetDetails()[0]; // eliminate wrapper float coordFactor = similarity.Coord(coord, maxCoord); if (coordFactor == 1.0f) // coord is no-op return sumExpl; // eliminate wrapper else { Explanation result = new Explanation(); result.SetDescription("product of:"); result.AddDetail(sumExpl); result.AddDetail(new Explanation(coordFactor, "coord(" + coord + "/" + maxCoord + ")")); result.SetValue(sum * coordFactor); return result; } }