private IList <SerializableExplanation> m_details; // sub-explanations public SerializableExplanation(Explanation explanation) { Value = explanation.Value; Description = explanation.Description; Explanation[] details = explanation.GetDetails(); if (details == null) { return; } foreach (Explanation exp in details) { AddDetail(new SerializableExplanation(exp)); } }
private void AddNode(TreeNode tn, Explanation e) { TreeNode node = new TreeNode(e.Value.ToString("G4") + " " + e.Description); 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]); } } }
/* * 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)); } } }