diff --git a/src/Amalgam/GeneralizedDistance.h b/src/Amalgam/GeneralizedDistance.h index 0cf9213e..9a64d1ed 100644 --- a/src/Amalgam/GeneralizedDistance.h +++ b/src/Amalgam/GeneralizedDistance.h @@ -35,7 +35,9 @@ class GeneralizedDistance enum EffectiveFeatureDifferenceType : uint32_t { - EFDT_NOMINAL, + //nominal values, but every nominal relationship is the same and symmetric: + //A is as different as B as B is as different as C + EFDT_NOMINAL_UNIVERSALLY_SYMMETRIC_PRECOMPUTED, //everything is precomputed from interned values that are looked up EFDT_VALUES_UNIVERSALLY_PRECOMPUTED, //continuous without cycles, but everything is always numeric @@ -57,7 +59,7 @@ class GeneralizedDistance { inversePValue = 1.0 / pValue; - ComputeNominalDistanceTerms(); + ComputeAndStoreUniversallySymmetricNominalDistanceTerms(); bool compute_approximate = NeedToPrecomputeApproximate(); if(compute_approximate) @@ -65,23 +67,6 @@ class GeneralizedDistance fastPowP = RepeatedFastPow(pValue); fastPowInverseP = RepeatedFastPow(inversePValue); } - - //default to the accuracy that should be used first - if(recomputeAccurateDistances) - SetHighAccuracy(false); - else - SetHighAccuracy(highAccuracy); - } - - //update usingHighAccuracy and nominal defaults - inline void SetHighAccuracy(bool high_accuracy) - { - //need to have asked for high_accuracy and have computed high accuracy - // or just not have computed low accuracy at all - if( (high_accuracy && NeedToPrecomputeAccurate())) - defaultPrecision = ExactApproxValuePair::EXACT; - else - defaultPrecision = ExactApproxValuePair::APPROX; } //computes and sets unknownToUnknownDistanceTerm and knownToUnknownDistanceTerm based on @@ -91,24 +76,19 @@ class GeneralizedDistance { bool compute_accurate = NeedToPrecomputeAccurate(); bool compute_approximate = NeedToPrecomputeApproximate(); - auto &feature_params = featureParams[index]; //compute unknownToUnknownDistanceTerm if(compute_accurate) { feature_params.unknownToUnknownDistanceTerm.SetValue( - ComputeDistanceTermNonNull(feature_params.unknownToUnknownDifference, - index, ExactApproxValuePair::EXACT), - ExactApproxValuePair::EXACT); + ComputeDistanceTermNonNull(feature_params.unknownToUnknownDifference, index, true), true); } if(compute_approximate) { feature_params.unknownToUnknownDistanceTerm.SetValue( - ComputeDistanceTermNonNull(feature_params.unknownToUnknownDifference, - index, ExactApproxValuePair::APPROX), - ExactApproxValuePair::APPROX); + ComputeDistanceTermNonNull(feature_params.unknownToUnknownDifference, index, false), false); } //if knownToUnknownDifference is same as unknownToUnknownDifference, can copy distance term instead of recomputing @@ -122,34 +102,30 @@ class GeneralizedDistance if(compute_accurate) { feature_params.knownToUnknownDistanceTerm.SetValue( - ComputeDistanceTermNonNull(feature_params.knownToUnknownDifference, - index, ExactApproxValuePair::EXACT), - ExactApproxValuePair::EXACT); + ComputeDistanceTermNonNull(feature_params.knownToUnknownDifference, index, true), true); } if(compute_approximate) { feature_params.knownToUnknownDistanceTerm.SetValue( - ComputeDistanceTermNonNull(feature_params.knownToUnknownDifference, - index, ExactApproxValuePair::APPROX), - ExactApproxValuePair::APPROX); + ComputeDistanceTermNonNull(feature_params.knownToUnknownDifference, index, false), false); } } if(HasNumberInternValues(index)) { - auto &precomputed_terms = feature_params.precomputedInternDistanceTerms; + auto &interned_dist_terms = feature_params.internDistanceTerms; if(target_value_is_null_equivalent) { - precomputed_terms[0] = feature_params.unknownToUnknownDistanceTerm.GetValue(defaultPrecision); - auto k_to_unk = feature_params.knownToUnknownDistanceTerm.GetValue(defaultPrecision); - for(size_t i = 1; i < precomputed_terms.size(); i++) - precomputed_terms[i] = k_to_unk; + interned_dist_terms[0] = feature_params.unknownToUnknownDistanceTerm; + auto k_to_unk = feature_params.knownToUnknownDistanceTerm; + for(size_t i = 1; i < interned_dist_terms.size(); i++) + interned_dist_terms[i] = k_to_unk; } else //just set the unknown value { - precomputed_terms[0] = feature_params.knownToUnknownDistanceTerm.GetValue(defaultPrecision); + interned_dist_terms[0] = feature_params.knownToUnknownDistanceTerm; } } } @@ -157,22 +133,31 @@ class GeneralizedDistance //for the feature index, computes and stores the distance terms as measured from value to each interned value inline void ComputeAndStoreInternedNumberValuesAndDistanceTerms(size_t index, double value, std::vector *interned_values) { + bool compute_accurate = NeedToPrecomputeAccurate(); + bool compute_approximate = NeedToPrecomputeApproximate(); auto &feature_params = featureParams[index]; feature_params.internedNumberIndexToNumberValue = interned_values; if(interned_values == nullptr) { - feature_params.precomputedInternDistanceTerms.clear(); + feature_params.internDistanceTerms.clear(); return; } - feature_params.precomputedInternDistanceTerms.resize(interned_values->size()); + feature_params.internDistanceTerms.resize(interned_values->size()); //first entry is known-unknown distance - feature_params.precomputedInternDistanceTerms[0] = ComputeDistanceTermKnownToUnknown(index); - for(size_t i = 1; i < feature_params.precomputedInternDistanceTerms.size(); i++) + if(compute_accurate) + feature_params.internDistanceTerms[0].SetValue(ComputeDistanceTermKnownToUnknown(index, true), true); + if(compute_approximate) + feature_params.internDistanceTerms[0].SetValue(ComputeDistanceTermKnownToUnknown(index, false), false); + + for(size_t i = 1; i < feature_params.internDistanceTerms.size(); i++) { double difference = value - interned_values->at(i); - feature_params.precomputedInternDistanceTerms[i] = ComputeDistanceTermNonNominalNonNullRegular(difference, index); + if(compute_accurate) + feature_params.internDistanceTerms[i].SetValue(ComputeDistanceTermNonNominalNonNullRegular(difference, index, true), true); + if(compute_approximate) + feature_params.internDistanceTerms[i].SetValue(ComputeDistanceTermNonNominalNonNullRegular(difference, index, false), false); } } @@ -206,9 +191,9 @@ class GeneralizedDistance //computes the Lukaszyk–Karmowski metric deviation component for the minkowski distance equation given the feature difference and feature deviation //assumes deviation is nonnegative - __forceinline double ComputeDeviationPart(const double diff, const double deviation) + __forceinline double ComputeDeviationPart(const double diff, const double deviation, bool high_accuracy) { - if(defaultPrecision == ExactApproxValuePair::EXACT) + if(high_accuracy) #ifdef DISTANCE_USE_LAPLACE_LK_METRIC return ComputeDeviationPartLaplace(diff, deviation); #else @@ -260,6 +245,11 @@ class GeneralizedDistance exactApproxPair = { initial_value, initial_value }; } + constexpr double GetValue(bool high_accuracy) + { + return exactApproxPair[high_accuracy ? EXACT : APPROX]; + } + constexpr double GetValue(int offset) { return exactApproxPair[offset]; @@ -270,111 +260,43 @@ class GeneralizedDistance exactApproxPair[offset] = value; } + __forceinline void SetValue(double value, bool high_accuracy) + { + exactApproxPair[high_accuracy ? EXACT : APPROX] = value; + } + std::array exactApproxPair; }; //update cached nominal deltas based on highAccuracy and recomputeAccurateDistances, caching what is needed given those flags - inline void ComputeNominalDistanceTerms() + inline void ComputeAndStoreUniversallySymmetricNominalDistanceTerms() { bool compute_accurate = NeedToPrecomputeAccurate(); bool compute_approximate = NeedToPrecomputeApproximate(); - //infinite pValue means take max or min, so just use 1 for computations below, - // and term aggregation (outside of this function) will take care of the rest - double effective_p_value = pValue; - if(pValue == std::numeric_limits::infinity() || pValue == -std::numeric_limits::infinity()) - effective_p_value = 1; - - const size_t num_features = featureParams.size(); - - //value of delta for nominal values when not using high accuracy, may be not exactly 1.0 due to using FastPow approximation for effective_p_values that aren't 1 - double nominal_approximate_diff = 1.0; - if(compute_approximate) - nominal_approximate_diff = ( (effective_p_value == 1) ? 1.0 : FastPowNonZeroExp(1.0, effective_p_value) ); - - for(size_t i = 0; i < num_features; i++) + for(size_t i = 0; i < featureParams.size(); i++) { auto &feat_params = featureParams[i]; - if(feat_params.featureType != FDT_NOMINAL) - continue; - - double weight = feat_params.weight; - - if(!DoesFeatureHaveDeviation(i)) + if(feat_params.featureType == FDT_NOMINAL) { - if(compute_accurate) + //ensure if a feature has deviations they're not too small to underflow + if(DoesFeatureHaveDeviation(i)) { - feat_params.nominalMatchDistanceTerm.SetValue(0.0, ExactApproxValuePair::EXACT); - - if(pValue != 0) - feat_params.nominalNonMatchDistanceTerm.SetValue(weight, ExactApproxValuePair::EXACT); - else //1.0 to any power is still 1.0 when computed exactly - feat_params.nominalNonMatchDistanceTerm.SetValue(1.0, ExactApproxValuePair::EXACT); - } - - if(compute_approximate) - { - feat_params.nominalMatchDistanceTerm.SetValue(0.0, ExactApproxValuePair::APPROX); - - if(effective_p_value != 0) - feat_params.nominalNonMatchDistanceTerm.SetValue(weight * nominal_approximate_diff, ExactApproxValuePair::APPROX); - else //pValue == 0 - feat_params.nominalNonMatchDistanceTerm.SetValue(FastPow(1.0, weight), ExactApproxValuePair::APPROX); + constexpr double smallest_delta = 1e-100; + if(feat_params.typeAttributes.nominalCount == 1 && feat_params.deviation < smallest_delta) + feat_params.deviation = smallest_delta; } - } - else //has deviations - { - double deviation = feat_params.deviation; - double nominal_count = feat_params.typeAttributes.nominalCount; - - // n = number of nominal classes - // match: deviation ^ p * weight - // non match: (deviation + (1 - deviation) / (n - 1)) ^ p * weight - //if there is only one nominal class, the smallest delta value it could be is the specified smallest delta, otherwise it's 1.0 - constexpr double smallest_delta = 1e-100; - if(nominal_count == 1 && deviation < smallest_delta) - deviation = smallest_delta; - - double mismatch_deviation = 1.0; - if(nominal_count > 1) - mismatch_deviation = (deviation + (1 - deviation) / (nominal_count - 1)); if(compute_accurate) { - if(effective_p_value == 1) - { - feat_params.nominalMatchDistanceTerm.SetValue(deviation * weight, ExactApproxValuePair::EXACT); - feat_params.nominalNonMatchDistanceTerm.SetValue(mismatch_deviation * weight, ExactApproxValuePair::EXACT); - } - else if(effective_p_value != 0) - { - feat_params.nominalMatchDistanceTerm.SetValue(std::pow(deviation, effective_p_value) * weight, ExactApproxValuePair::EXACT); - feat_params.nominalNonMatchDistanceTerm.SetValue(std::pow(mismatch_deviation, effective_p_value) * weight, ExactApproxValuePair::EXACT); - } - else //pValue == 0 - { - feat_params.nominalMatchDistanceTerm.SetValue(std::pow(deviation, weight), ExactApproxValuePair::EXACT); - feat_params.nominalNonMatchDistanceTerm.SetValue(std::pow(mismatch_deviation, weight), ExactApproxValuePair::EXACT); - } + feat_params.nominalMatchDistanceTerm.SetValue(ComputeDistanceTermNominalUniversallySymmetricExactMatch(i, true), true); + feat_params.nominalNonMatchDistanceTerm.SetValue(ComputeDistanceTermNominalUniversallySymmetricNonMatch(i, true), true); } if(compute_approximate) { - if(effective_p_value == 1) - { - feat_params.nominalMatchDistanceTerm.SetValue(deviation * weight, ExactApproxValuePair::APPROX); - feat_params.nominalNonMatchDistanceTerm.SetValue(mismatch_deviation * weight, ExactApproxValuePair::APPROX); - } - else if(effective_p_value != 0) - { - feat_params.nominalMatchDistanceTerm.SetValue(FastPow(deviation, effective_p_value) * weight, ExactApproxValuePair::APPROX); - feat_params.nominalNonMatchDistanceTerm.SetValue(FastPow(mismatch_deviation, effective_p_value) * weight, ExactApproxValuePair::APPROX); - } - else //pValue == 0 - { - feat_params.nominalMatchDistanceTerm.SetValue(FastPow(deviation, weight), ExactApproxValuePair::APPROX); - feat_params.nominalNonMatchDistanceTerm.SetValue(FastPow(mismatch_deviation, weight), ExactApproxValuePair::APPROX); - } + feat_params.nominalMatchDistanceTerm.SetValue(ComputeDistanceTermNominalUniversallySymmetricExactMatch(i, false), false); + feat_params.nominalNonMatchDistanceTerm.SetValue(ComputeDistanceTermNominalUniversallySymmetricNonMatch(i, false), false); } } } @@ -417,7 +339,7 @@ class GeneralizedDistance } //computes the exponentiation of d to 1/p - __forceinline double InverseExponentiateDistance(double d) + __forceinline double InverseExponentiateDistance(double d, bool high_accuracy) { if(pValue == 1) return d; @@ -425,14 +347,14 @@ class GeneralizedDistance if(pValue == 0.5) return d * d; - if(defaultPrecision == ExactApproxValuePair::EXACT) + if(high_accuracy) return std::pow(d, inversePValue); else return fastPowInverseP.FastPow(d); } //computes the exponentiation of d to p given precision being from ExactApproxValuePair - __forceinline double ExponentiateDifferenceTerm(double d, int precision) + __forceinline double ExponentiateDifferenceTerm(double d, bool high_accuracy) { if(pValue == 1) return d; @@ -440,7 +362,7 @@ class GeneralizedDistance if(pValue == 2) return d * d; - if(precision == ExactApproxValuePair::EXACT) + if(high_accuracy) return std::pow(d, pValue); else return fastPowP.FastPow(d); @@ -465,28 +387,125 @@ class GeneralizedDistance return -std::numeric_limits::infinity(); } } - //computes the distance term for a nominal when two nominals are equal - __forceinline double ComputeDistanceTermNominalExactMatch(size_t index) + + //computes the distance term for a nominal when two universally symmetric nominals are equal + __forceinline double ComputeDistanceTermNominalUniversallySymmetricExactMatch(size_t index, bool high_accuracy) + { + if(!DoesFeatureHaveDeviation(index)) + return 0.0; + + double weight = featureParams[index].weight; + double deviation = featureParams[index].deviation; + + //infinite pValues are treated the same as 1 for distance terms, + //and are the same value regardless of high_accuracy + if(pValue == 1 || pValue == std::numeric_limits::infinity() + || pValue == -std::numeric_limits::infinity()) + return deviation * weight; + + if(pValue == 0) + { + if(high_accuracy) + return std::pow(deviation, weight); + else + return FastPow(deviation, weight); + } + else + { + if(high_accuracy) + return std::pow(deviation, pValue) * weight; + else + return FastPow(deviation, pValue) * weight; + } + } + + //computes the distance term for a nominal when two universally symmetric nominals are not equal + __forceinline double ComputeDistanceTermNominalUniversallySymmetricNonMatch(size_t index, bool high_accuracy) + { + double weight = featureParams[index].weight; + if(DoesFeatureHaveDeviation(index)) + { + double deviation = featureParams[index].deviation; + double nominal_count = featureParams[index].typeAttributes.nominalCount; + + // n = number of nominal classes + // match: deviation ^ p * weight + // non match: (deviation + (1 - deviation) / (n - 1)) ^ p * weight + //if there is only one nominal class, the smallest delta value it could be is the specified smallest delta, otherwise it's 1.0 + double mismatch_deviation = 1.0; + if(nominal_count > 1) + mismatch_deviation = (deviation + (1 - deviation) / (nominal_count - 1)); + + //infinite pValues are treated the same as 1 for distance terms, + //and are the same value regardless of high_accuracy + if(pValue == 1 || pValue == std::numeric_limits::infinity() + || pValue == -std::numeric_limits::infinity()) + return mismatch_deviation * weight; + + if(pValue == 0) + { + if(high_accuracy) + return std::pow(mismatch_deviation, weight); + else + return FastPow(mismatch_deviation, weight); + } + else + { + if(high_accuracy) + return std::pow(mismatch_deviation, pValue) * weight; + else + return FastPow(mismatch_deviation, pValue) * weight; + } + } + else + { + if(high_accuracy) + { + if(pValue != 0.0) + return weight; + else + return 1.0; + } + else + { + if(pValue != 0.0) + { + //special handling for infinities + if(pValue == std::numeric_limits::infinity() || pValue == -std::numeric_limits::infinity()) + return weight; + else //since FastPow isn't exact for 1.0, need to compute the value + return weight * FastPowNonZeroExp(1.0, pValue); + } + else //pValue == 0.0 + { + return FastPow(1.0, weight); + } + } + } + } + + //returns the precomputed distance term for a nominal when two universally symmetric nominals are equal + __forceinline double ComputeDistanceTermNominalUniversallySymmetricExactMatchPrecomputed(size_t index, bool high_accuracy) { - return featureParams[index].nominalMatchDistanceTerm.GetValue(defaultPrecision); + return featureParams[index].nominalMatchDistanceTerm.GetValue(high_accuracy); } - //computes the distance term for a nominal when two nominals are not equal - __forceinline double ComputeDistanceTermNominalNonMatch(size_t index) + //returns the precomputed distance term for a nominal when two universally symmetric nominals are not equal + __forceinline double ComputeDistanceTermNominalUniversallySymmetricNonMatchPrecomputed(size_t index, bool high_accuracy) { - return featureParams[index].nominalNonMatchDistanceTerm.GetValue(defaultPrecision); + return featureParams[index].nominalNonMatchDistanceTerm.GetValue(high_accuracy); } //computes the distance term for an unknown-unknown - __forceinline double ComputeDistanceTermUnknownToUnknown(size_t index) + __forceinline double ComputeDistanceTermUnknownToUnknown(size_t index, bool high_accuracy) { - return featureParams[index].unknownToUnknownDistanceTerm.GetValue(defaultPrecision); + return featureParams[index].unknownToUnknownDistanceTerm.GetValue(high_accuracy); } //computes the distance term for an known-unknown - __forceinline double ComputeDistanceTermKnownToUnknown(size_t index) + __forceinline double ComputeDistanceTermKnownToUnknown(size_t index, bool high_accuracy) { - return featureParams[index].knownToUnknownDistanceTerm.GetValue(defaultPrecision); + return featureParams[index].knownToUnknownDistanceTerm.GetValue(high_accuracy); } //returns true if the feature at index has interned number values @@ -496,26 +515,26 @@ class GeneralizedDistance } //returns the precomputed distance term for the interned number with intern_value_index - __forceinline double ComputeDistanceTermNumberInterned(size_t intern_value_index, size_t index) + __forceinline double ComputeDistanceTermNumberInternedPrecomputed(size_t intern_value_index, size_t index, bool high_accuracy) { - return featureParams[index].precomputedInternDistanceTerms[intern_value_index]; + return featureParams[index].internDistanceTerms[intern_value_index].GetValue(high_accuracy); } //computes the inner term for a non-nominal with an exact match of values - __forceinline double ComputeDistanceTermNonNominalExactMatch(size_t index) + __forceinline double ComputeDistanceTermNonNominalExactMatch(size_t index, bool high_accuracy) { if(!DoesFeatureHaveDeviation(index)) return 0.0; //apply deviations - double diff = ComputeDeviationPart(0.0, featureParams[index].deviation); + double diff = ComputeDeviationPart(0.0, featureParams[index].deviation, high_accuracy); //exponentiate and return with weight - return ExponentiateDifferenceTerm(diff, defaultPrecision) * featureParams[index].weight; + return ExponentiateDifferenceTerm(diff, high_accuracy) * featureParams[index].weight; } //computes the base of the difference between two values non-nominal (e.g., continuous) - __forceinline double ComputeDifferenceTermBaseNonNominal(double diff, size_t index) + __forceinline double ComputeDifferenceTermBaseNonNominal(double diff, size_t index, bool high_accuracy) { //compute absolute value diff = std::abs(diff); @@ -526,171 +545,188 @@ class GeneralizedDistance //apply deviations if(DoesFeatureHaveDeviation(index)) - diff += ComputeDeviationPart(diff, featureParams[index].deviation); + diff += ComputeDeviationPart(diff, featureParams[index].deviation, high_accuracy); return diff; } //computes the base of the difference between two values non-nominal (e.g., continuous) that isn't cyclic - __forceinline double ComputeDifferenceTermBaseNonNominalNonCyclic(double diff, size_t index) + __forceinline double ComputeDifferenceTermBaseNonNominalNonCyclic(double diff, size_t index, bool high_accuracy) { //compute absolute value diff = std::abs(diff); //apply deviations if(DoesFeatureHaveDeviation(index)) - diff += ComputeDeviationPart(diff, featureParams[index].deviation); + diff += ComputeDeviationPart(diff, featureParams[index].deviation, high_accuracy); return diff; } //computes the distance term for a non-nominal (e.g., continuous) for p non-zero and non-infinite with no nulls // diff can be negative - __forceinline double ComputeDistanceTermNonNominalNonNullRegular(double diff, size_t index) + __forceinline double ComputeDistanceTermNonNominalNonNullRegular(double diff, size_t index, bool high_accuracy) { - diff = ComputeDifferenceTermBaseNonNominal(diff, index); + diff = ComputeDifferenceTermBaseNonNominal(diff, index, high_accuracy); //exponentiate and return with weight - return ExponentiateDifferenceTerm(diff, defaultPrecision) * featureParams[index].weight; + return ExponentiateDifferenceTerm(diff, high_accuracy) * featureParams[index].weight; } //computes the distance term for a non-nominal (e.g., continuous) for p non-zero and non-infinite with max of one null // diff can be negative - __forceinline double ComputeDistanceTermNonNominalOneNonNullRegular(double diff, size_t index) + __forceinline double ComputeDistanceTermNonNominalOneNonNullRegular(double diff, size_t index, bool high_accuracy) { - diff = ComputeDifferenceTermBaseNonNominal(diff, index); + diff = ComputeDifferenceTermBaseNonNominal(diff, index, high_accuracy); //exponentiate and return with weight - return ExponentiateDifferenceTerm(diff, defaultPrecision) * featureParams[index].weight; + return ExponentiateDifferenceTerm(diff, high_accuracy) * featureParams[index].weight; } //computes the distance term for a non-nominal (e.g., continuous) for p non-zero and non-infinite that isn't cyclic with no nulls // diff can be negative - __forceinline double ComputeDistanceTermNonNominalNonCyclicNonNullRegular(double diff, size_t index) + __forceinline double ComputeDistanceTermNonNominalNonCyclicNonNullRegular(double diff, size_t index, bool high_accuracy) { - diff = ComputeDifferenceTermBaseNonNominalNonCyclic(diff, index); + diff = ComputeDifferenceTermBaseNonNominalNonCyclic(diff, index, high_accuracy); //exponentiate and return with weight - return ExponentiateDifferenceTerm(diff, defaultPrecision) * featureParams[index].weight; + return ExponentiateDifferenceTerm(diff, high_accuracy) * featureParams[index].weight; } //computes the distance term for a non-nominal (e.g., continuous) for p non-zero and non-infinite that isn't cyclic with max of one null // diff can be negative - __forceinline double ComputeDistanceTermNonNominalNonCyclicOneNonNullRegular(double diff, size_t index) + __forceinline double ComputeDistanceTermNonNominalNonCyclicOneNonNullRegular(double diff, size_t index, bool high_accuracy) { if(FastIsNaN(diff)) - return ComputeDistanceTermKnownToUnknown(index); + return ComputeDistanceTermKnownToUnknown(index, high_accuracy); - diff = ComputeDifferenceTermBaseNonNominalNonCyclic(diff, index); + diff = ComputeDifferenceTermBaseNonNominalNonCyclic(diff, index, high_accuracy); //exponentiate and return with weight - return ExponentiateDifferenceTerm(diff, defaultPrecision) * featureParams[index].weight; + return ExponentiateDifferenceTerm(diff, high_accuracy) * featureParams[index].weight; } //computes the inner term of the Minkowski norm summation for a single index for p=0 __forceinline double ComputeDistanceTermP0(EvaluableNodeImmediateValue a, EvaluableNodeImmediateValue b, - EvaluableNodeImmediateValueType a_type, EvaluableNodeImmediateValueType b_type, size_t index) + EvaluableNodeImmediateValueType a_type, EvaluableNodeImmediateValueType b_type, size_t index, bool high_accuracy) { double diff = ComputeDifference(a, b, a_type, b_type, featureParams[index].featureType); if(FastIsNaN(diff)) - return LookupNullDistanceTerm(a, b, a_type, b_type, index); + return LookupNullDistanceTerm(a, b, a_type, b_type, index, high_accuracy); //if nominal, don't need to compute absolute value of diff because just need to compare to 0 if(IsFeatureNominal(index)) - return (diff == 0.0) ? ComputeDistanceTermNominalExactMatch(index) : ComputeDistanceTermNominalNonMatch(index); + return (diff == 0.0) ? ComputeDistanceTermNominalUniversallySymmetricExactMatchPrecomputed(index, high_accuracy) + : ComputeDistanceTermNominalUniversallySymmetricNonMatchPrecomputed(index, high_accuracy); - diff = ComputeDifferenceTermBaseNonNominal(diff, index); + diff = ComputeDifferenceTermBaseNonNominal(diff, index, high_accuracy); - return std::pow(diff, featureParams[index].weight); + if(high_accuracy) + return std::pow(diff, featureParams[index].weight); + else + return FastPow(diff, featureParams[index].weight); } //computes the inner term of the Minkowski norm summation for a single index for p=infinity or -infinity __forceinline double ComputeDistanceTermPInf(EvaluableNodeImmediateValue a, EvaluableNodeImmediateValue b, - EvaluableNodeImmediateValueType a_type, EvaluableNodeImmediateValueType b_type, size_t index) + EvaluableNodeImmediateValueType a_type, EvaluableNodeImmediateValueType b_type, size_t index, bool high_accuracy) { double diff = ComputeDifference(a, b, a_type, b_type, featureParams[index].featureType); if(FastIsNaN(diff)) - return LookupNullDistanceTerm(a, b, a_type, b_type, index); + return LookupNullDistanceTerm(a, b, a_type, b_type, index, high_accuracy); //if nominal, don't need to compute absolute value of diff because just need to compare to 0 if(IsFeatureNominal(index)) - return (diff == 0.0) ? ComputeDistanceTermNominalExactMatch(index) : ComputeDistanceTermNominalNonMatch(index); + return (diff == 0.0) ? ComputeDistanceTermNominalUniversallySymmetricExactMatchPrecomputed(index, high_accuracy) + : ComputeDistanceTermNominalUniversallySymmetricNonMatchPrecomputed(index, high_accuracy); - diff = ComputeDifferenceTermBaseNonNominal(diff, index); + diff = ComputeDifferenceTermBaseNonNominal(diff, index, high_accuracy); return diff * featureParams[index].weight; } //computes the inner term of the Minkowski norm summation for a single index regardless of pValue - __forceinline double ComputeDistanceTermNonNull(double diff, size_t index, int precision) + __forceinline double ComputeDistanceTermNonNull(double diff, size_t index, bool high_accuracy) { if(!IsFeatureNominal(index)) - diff = ComputeDifferenceTermBaseNonNominal(diff, index); + diff = ComputeDifferenceTermBaseNonNominal(diff, index, high_accuracy); if(pValue == 0.0) - return std::pow(diff, featureParams[index].weight); + { + if(high_accuracy) + return std::pow(diff, featureParams[index].weight); + else + return FastPow(diff, featureParams[index].weight); + } else if(pValue == std::numeric_limits::infinity() || pValue == -std::numeric_limits::infinity()) return diff * featureParams[index].weight; else - return ExponentiateDifferenceTerm(diff, precision) * featureParams[index].weight; + return ExponentiateDifferenceTerm(diff, high_accuracy) * featureParams[index].weight; } //computes the inner term of the Minkowski norm summation for a single index for p non-zero and non-infinite //where at least one of the values is non-null - __forceinline double ComputeDistanceTermRegularOneNonNull(double diff, size_t index) + __forceinline double ComputeDistanceTermRegularOneNonNull(double diff, size_t index, bool high_accuracy) { if(FastIsNaN(diff)) - return ComputeDistanceTermKnownToUnknown(index); + return ComputeDistanceTermKnownToUnknown(index, high_accuracy); //if nominal, don't need to compute absolute value of diff because just need to compare to 0 if(IsFeatureNominal(index)) - return (diff == 0.0) ? ComputeDistanceTermNominalExactMatch(index) : ComputeDistanceTermNominalNonMatch(index); + return (diff == 0.0) ? ComputeDistanceTermNominalUniversallySymmetricExactMatchPrecomputed(index, high_accuracy) + : ComputeDistanceTermNominalUniversallySymmetricNonMatchPrecomputed(index, high_accuracy); - return ComputeDistanceTermNonNominalNonNullRegular(diff, index); + return ComputeDistanceTermNonNominalNonNullRegular(diff, index, high_accuracy); } //computes the inner term of the Minkowski norm summation for a single index for p non-zero and non-infinite __forceinline double ComputeDistanceTermRegular(EvaluableNodeImmediateValue a, EvaluableNodeImmediateValue b, - EvaluableNodeImmediateValueType a_type, EvaluableNodeImmediateValueType b_type, size_t index) + EvaluableNodeImmediateValueType a_type, EvaluableNodeImmediateValueType b_type, size_t index, bool high_accuracy) { double diff = ComputeDifference(a, b, a_type, b_type, featureParams[index].featureType); if(FastIsNaN(diff)) - return LookupNullDistanceTerm(a, b, a_type, b_type, index); + return LookupNullDistanceTerm(a, b, a_type, b_type, index, high_accuracy); //if nominal, don't need to compute absolute value of diff because just need to compare to 0 if(IsFeatureNominal(index)) - return (diff == 0.0) ? ComputeDistanceTermNominalExactMatch(index) : ComputeDistanceTermNominalNonMatch(index); + return (diff == 0.0) ? ComputeDistanceTermNominalUniversallySymmetricExactMatchPrecomputed(index, high_accuracy) + : ComputeDistanceTermNominalUniversallySymmetricNonMatchPrecomputed(index, high_accuracy); - return ComputeDistanceTermNonNominalNonNullRegular(diff, index); + return ComputeDistanceTermNonNominalNonNullRegular(diff, index, high_accuracy); } //computes the inner term of the Minkowski norm summation for a single index that isn't null, //but computes only from the distance (does not take into account feature measurement type) - __forceinline double ComputeDistanceTermFromNonNullDifferenceOnly(double diff, size_t index) + __forceinline double ComputeDistanceTermFromNonNullDifferenceOnly(double diff, size_t index, bool high_accuracy) { if(pValue == 0.0) - return std::pow(diff, featureParams[index].weight); + { + if(high_accuracy) + return std::pow(diff, featureParams[index].weight); + else + return FastPow(diff, featureParams[index].weight); + } else if(pValue == std::numeric_limits::infinity() || pValue == -std::numeric_limits::infinity()) return diff * featureParams[index].weight; else - return ExponentiateDifferenceTerm(diff, defaultPrecision) * featureParams[index].weight; + return ExponentiateDifferenceTerm(diff, high_accuracy) * featureParams[index].weight; } //returns the distance term for the either one or two unknown values __forceinline double LookupNullDistanceTerm(EvaluableNodeImmediateValue a, EvaluableNodeImmediateValue b, - EvaluableNodeImmediateValueType a_type, EvaluableNodeImmediateValueType b_type, size_t index) + EvaluableNodeImmediateValueType a_type, EvaluableNodeImmediateValueType b_type, size_t index, bool high_accuracy) { bool a_unknown = (a_type == ENIVT_NULL || (a_type == ENIVT_NUMBER && FastIsNaN(a.number))); bool b_unknown = (b_type == ENIVT_NULL || (b_type == ENIVT_NUMBER && FastIsNaN(b.number))); if(a_unknown && b_unknown) - return ComputeDistanceTermUnknownToUnknown(index); + return ComputeDistanceTermUnknownToUnknown(index, high_accuracy); if(a_unknown || b_unknown) - return ComputeDistanceTermKnownToUnknown(index); + return ComputeDistanceTermKnownToUnknown(index, high_accuracy); //incompatible types, use whichever is further - return std::max(ComputeDistanceTermUnknownToUnknown(index), ComputeDistanceTermKnownToUnknown(index)); + return std::max(ComputeDistanceTermUnknownToUnknown(index, high_accuracy), ComputeDistanceTermKnownToUnknown(index, high_accuracy)); } //computes the difference between a and b given their types and the distance_type and the feature difference type @@ -772,7 +808,7 @@ class GeneralizedDistance //if deviations.size() == 0, no deviations are used, else deviations.size() must == a.size() == b.size() // -uses per-feature deviations: per-feature deviation is added after the distance between ai and bi is computed __forceinline double ComputeMinkowskiDistance(std::vector &a, std::vector &a_types, - std::vector &b, std::vector &b_types) + std::vector &b, std::vector &b_types, bool high_accuracy) { if(a.size() != b.size()) return std::numeric_limits::quiet_NaN(); @@ -781,7 +817,7 @@ class GeneralizedDistance { double dist_accum = 1.0; for(size_t i = 0; i < a.size(); i++) - dist_accum *= ComputeDistanceTermP0(a[i], b[i], a_types[i], b_types[i], i); + dist_accum *= ComputeDistanceTermP0(a[i], b[i], a_types[i], b_types[i], i, high_accuracy); return dist_accum; } @@ -791,7 +827,7 @@ class GeneralizedDistance for(size_t i = 0; i < a.size(); i++) { - double term = ComputeDistanceTermPInf(a[i], b[i], a_types[i], b_types[i], i); + double term = ComputeDistanceTermPInf(a[i], b[i], a_types[i], b_types[i], i, high_accuracy); if(term > max_term) max_term = term; @@ -805,7 +841,7 @@ class GeneralizedDistance for(size_t i = 0; i < a.size(); i++) { - double term = ComputeDistanceTermPInf(a[i], b[i], a_types[i], b_types[i], i); + double term = ComputeDistanceTermPInf(a[i], b[i], a_types[i], b_types[i], i, high_accuracy); if(term < min_term) min_term = term; @@ -817,9 +853,9 @@ class GeneralizedDistance { double dist_accum = 0.0; for(size_t i = 0; i < a.size(); i++) - dist_accum += ComputeDistanceTermRegular(a[i], b[i], a_types[i], b_types[i], i); + dist_accum += ComputeDistanceTermRegular(a[i], b[i], a_types[i], b_types[i], i, high_accuracy); - return InverseExponentiateDistance(dist_accum); + return InverseExponentiateDistance(dist_accum, high_accuracy); } } @@ -858,7 +894,7 @@ class GeneralizedDistance std::vector *internedNumberIndexToNumberValue; //precomputed distance terms for each interned value looked up by intern index - std::vector precomputedInternDistanceTerms; + std::vector internDistanceTerms; //type attributes dependent on featureType union @@ -898,9 +934,6 @@ class GeneralizedDistance //computed inverse of pValue double inversePValue; - //the current precision for exact vs approximate terms - int defaultPrecision; - //if true, then all computations should be performed with high accuracy bool highAccuracy; //if true, then estimates should be computed with low accuracy, but final results with high accuracy diff --git a/src/Amalgam/SeparableBoxFilterDataStore.cpp b/src/Amalgam/SeparableBoxFilterDataStore.cpp index 4097419b..3a12eaf7 100644 --- a/src/Amalgam/SeparableBoxFilterDataStore.cpp +++ b/src/Amalgam/SeparableBoxFilterDataStore.cpp @@ -293,6 +293,7 @@ void SeparableBoxFilterDataStore::FindEntitiesWithinDistance(GeneralizedDistance //Starting with all entities, narrow down the list by incrementally summing up the minkowski distances const double max_dist_exponentiated = std::pow(max_dist, dist_params.pValue); //max_dist ^ p >= MinkowskiDistanceSum + bool high_accuracy = dist_params.highAccuracy; //initialize all distances to 0 auto &distances = parametersAndBuffers.entityDistances; @@ -311,8 +312,8 @@ void SeparableBoxFilterDataStore::FindEntitiesWithinDistance(GeneralizedDistance if(target_value_type == ENIVT_NULL || (target_value_type == ENIVT_NUMBER && FastIsNaN(target_value.number)) ) { //add the appropriate unknown distance to each element - double unknown_unknown_term = dist_params.ComputeDistanceTermUnknownToUnknown(query_feature_index); - double known_unknown_term = dist_params.ComputeDistanceTermKnownToUnknown(query_feature_index); + double unknown_unknown_term = dist_params.ComputeDistanceTermUnknownToUnknown(query_feature_index, high_accuracy); + double known_unknown_term = dist_params.ComputeDistanceTermKnownToUnknown(query_feature_index, high_accuracy); auto &null_indices = column_data->nullIndices; auto &nan_indices = column_data->nanIndices; @@ -342,7 +343,8 @@ void SeparableBoxFilterDataStore::FindEntitiesWithinDistance(GeneralizedDistance for(auto &value_entry : column_data->sortedNumberValueEntries) { //get distance term that is applicable to each entity in this bucket - double distance_term = dist_params.ComputeDistanceTermRegularOneNonNull(target_value.number - value_entry->value.number, query_feature_index); + double distance_term = dist_params.ComputeDistanceTermRegularOneNonNull( + target_value.number - value_entry->value.number, query_feature_index, high_accuracy); //for each bucket, add term to their sums for(auto entity_index : value_entry->indicesWithValue) @@ -359,7 +361,7 @@ void SeparableBoxFilterDataStore::FindEntitiesWithinDistance(GeneralizedDistance } //populate all non-number distances - double unknown_dist = dist_params.ComputeDistanceTermKnownToUnknown(query_feature_index); + double unknown_dist = dist_params.ComputeDistanceTermKnownToUnknown(query_feature_index, high_accuracy); for(auto entity_index : enabled_indices) { //skip over number values @@ -380,7 +382,6 @@ void SeparableBoxFilterDataStore::FindEntitiesWithinDistance(GeneralizedDistance //if target_value_type == ENIVT_CODE or ENIVT_STRING_ID, just compute all // won't save much for code until cache equal values // won't save much for string ids because it's just a lookup (though could make it a little faster by streamlining a specialized string loop) - //else, there are less indices to consider than possible unique values, so save computation by just considering entities that are still valid for(auto entity_index : enabled_indices) { @@ -388,7 +389,8 @@ void SeparableBoxFilterDataStore::FindEntitiesWithinDistance(GeneralizedDistance auto value = column_data->GetResolvedValue(value_type, GetValue(entity_index, absolute_feature_index)); value_type = column_data->GetResolvedValueType(value_type); - distances[entity_index] += dist_params.ComputeDistanceTermRegular(target_value, value, target_value_type, value_type, query_feature_index); + distances[entity_index] += dist_params.ComputeDistanceTermRegular( + target_value, value, target_value_type, value_type, query_feature_index, high_accuracy); //remove entity if its distance is already greater than the max_dist if(!(distances[entity_index] <= max_dist_exponentiated)) //false for NaN indices as well so they will be removed @@ -401,14 +403,14 @@ void SeparableBoxFilterDataStore::FindEntitiesWithinDistance(GeneralizedDistance bool need_recompute_distances = (dist_params.recomputeAccurateDistances && !dist_params.highAccuracy); if(!need_recompute_distances) { + high_accuracy = (dist_params.recomputeAccurateDistances || dist_params.highAccuracy); for(auto index : enabled_indices) - distances_out.emplace_back(dist_params.InverseExponentiateDistance(distances[index]), index); + distances_out.emplace_back(dist_params.InverseExponentiateDistance(distances[index], high_accuracy), index); } else { - dist_params.SetHighAccuracy(true); for(auto index : enabled_indices) - distances_out.emplace_back(GetDistanceBetween(dist_params, target_values, target_value_types, target_column_indices, index), index); + distances_out.emplace_back(GetDistanceBetween(dist_params, target_values, target_value_types, target_column_indices, index, true), index); } } @@ -462,6 +464,7 @@ void SeparableBoxFilterDataStore::FindEntitiesNearestToIndexedEntity(Generalized PopulateUnknownFeatureValueTerms(*dist_params); size_t num_enabled_features = target_values.size(); + bool high_accuracy = dist_params->highAccuracy; //make a copy of the entities so that the list can be modified BitArrayIntegerSet &possible_knn_indices = parametersAndBuffers.potentialMatchesSet; @@ -503,7 +506,8 @@ void SeparableBoxFilterDataStore::FindEntitiesNearestToIndexedEntity(Generalized //insert random selection into results heap double distance = ResolveDistanceToNonMatchTargetValues(*dist_params, - target_column_indices, target_values, target_value_types, partial_sums, entity_index, num_enabled_features); + target_column_indices, target_values, target_value_types, + partial_sums, entity_index, num_enabled_features, high_accuracy); sorted_results.Push(DistanceReferencePair(distance, entity_index)); //skip this entity in the next loops @@ -520,7 +524,8 @@ void SeparableBoxFilterDataStore::FindEntitiesNearestToIndexedEntity(Generalized size_t random_index = possible_knn_indices.GetRandomElement(rand_stream); double distance = ResolveDistanceToNonMatchTargetValues(*dist_params, - target_column_indices, target_values, target_value_types, partial_sums, random_index, num_enabled_features); + target_column_indices, target_values, target_value_types, + partial_sums, random_index, num_enabled_features, high_accuracy); sorted_results.Push(DistanceReferencePair(distance, random_index)); //skip this entity in the next loops @@ -544,7 +549,8 @@ void SeparableBoxFilterDataStore::FindEntitiesNearestToIndexedEntity(Generalized if(worst_candidate_distance == std::numeric_limits::infinity()) { double distance = ResolveDistanceToNonMatchTargetValues(*dist_params, - target_column_indices, target_values, target_value_types, partial_sums, entity_index, num_enabled_features); + target_column_indices, target_values, target_value_types, + partial_sums, entity_index, num_enabled_features, high_accuracy); sorted_results.Push(DistanceReferencePair(distance, entity_index)); //if full, update worst_candidate_distance @@ -561,8 +567,9 @@ void SeparableBoxFilterDataStore::FindEntitiesNearestToIndexedEntity(Generalized //already have enough elements, but see if this one is good enough auto [accept, distance] = ResolveDistanceToNonMatchTargetValues(*dist_params, - target_column_indices, target_values, target_value_types, partial_sums, - entity_index, min_distance_by_unpopulated_count, num_enabled_features, worst_candidate_distance, min_unpopulated_distances); + target_column_indices, target_values, target_value_types, + partial_sums, entity_index, min_distance_by_unpopulated_count, num_enabled_features, + worst_candidate_distance, min_unpopulated_distances, high_accuracy); if(!accept) continue; @@ -588,17 +595,16 @@ void SeparableBoxFilterDataStore::FindEntitiesNearestToIndexedEntity(Generalized //return k nearest -- don't need to clear because the values will be clobbered distances_out.resize(sorted_results.Size()); bool need_recompute_distances = (dist_params->recomputeAccurateDistances && !dist_params->highAccuracy); - if(need_recompute_distances) - dist_params->SetHighAccuracy(true); + high_accuracy = (dist_params->recomputeAccurateDistances || dist_params->highAccuracy); while(sorted_results.Size() > 0) { auto &drp = sorted_results.Top(); double distance; if(!need_recompute_distances) - distance = dist_params->InverseExponentiateDistance(drp.distance); + distance = dist_params->InverseExponentiateDistance(drp.distance, high_accuracy); else - distance = GetDistanceBetween(*dist_params, target_values, target_value_types, target_column_indices, drp.reference); + distance = GetDistanceBetween(*dist_params, target_values, target_value_types, target_column_indices, drp.reference, true); distances_out[sorted_results.Size() - 1] = DistanceReferencePair(distance, drp.reference); sorted_results.Pop(); @@ -632,6 +638,7 @@ void SeparableBoxFilterDataStore::FindNearestEntities(GeneralizedDistance &dist_ //one past the maximum entity index to be considered size_t end_index = enabled_indices.GetEndInteger(); + bool high_accuracy = dist_params.highAccuracy; //reuse the appropriate partial_sums_buffer buffer auto &partial_sums = parametersAndBuffers.partialSums; @@ -662,7 +669,8 @@ void SeparableBoxFilterDataStore::FindNearestEntities(GeneralizedDistance &dist_ enabled_indices.erase(good_match_index); double distance = ResolveDistanceToNonMatchTargetValues(dist_params, - target_column_indices, target_values, target_value_types, partial_sums, good_match_index, num_enabled_features); + target_column_indices, target_values, target_value_types, + partial_sums, good_match_index, num_enabled_features, high_accuracy); sorted_results.Push(DistanceReferencePair(distance, good_match_index)); } @@ -677,7 +685,8 @@ void SeparableBoxFilterDataStore::FindNearestEntities(GeneralizedDistance &dist_ enabled_indices.erase(random_index); double distance = ResolveDistanceToNonMatchTargetValues(dist_params, - target_column_indices, target_values, target_value_types, partial_sums, random_index, num_enabled_features); + target_column_indices, target_values, target_value_types, + partial_sums, random_index, num_enabled_features, high_accuracy); sorted_results.Push(DistanceReferencePair(distance, random_index)); } @@ -697,8 +706,9 @@ void SeparableBoxFilterDataStore::FindNearestEntities(GeneralizedDistance &dist_ continue; auto [accept, distance] = ResolveDistanceToNonMatchTargetValues(dist_params, - target_column_indices, target_values, target_value_types, partial_sums, entity_index, - min_distance_by_unpopulated_count, num_enabled_features, worst_candidate_distance, min_unpopulated_distances); + target_column_indices, target_values, target_value_types, partial_sums, + entity_index, min_distance_by_unpopulated_count, num_enabled_features, + worst_candidate_distance, min_unpopulated_distances, high_accuracy); if(accept) worst_candidate_distance = sorted_results.PushAndPop(DistanceReferencePair(distance, entity_index)).distance; @@ -714,7 +724,7 @@ void SeparableBoxFilterDataStore::FindNearestEntities(GeneralizedDistance &dist_ if(target_value_types[i] == ENIVT_NULL || (target_value_types[i] == ENIVT_NUMBER && FastIsNaN(target_values[i].number))) continue; - if(dist_params.ComputeDistanceTermKnownToUnknown(i) > worst_candidate_distance) + if(dist_params.ComputeDistanceTermKnownToUnknown(i, high_accuracy) > worst_candidate_distance) { auto &column = columnData[target_column_indices[i]]; auto &null_indices = column->nullIndices; @@ -752,8 +762,9 @@ void SeparableBoxFilterDataStore::FindNearestEntities(GeneralizedDistance &dist_ continue; auto [accept, distance] = ResolveDistanceToNonMatchTargetValues(dist_params, - target_column_indices, target_values, target_value_types, partial_sums, entity_index, - min_distance_by_unpopulated_count, num_enabled_features, worst_candidate_distance, min_unpopulated_distances); + target_column_indices, target_values, target_value_types, + partial_sums, entity_index, min_distance_by_unpopulated_count, num_enabled_features, + worst_candidate_distance, min_unpopulated_distances, high_accuracy); if(!accept) continue; @@ -783,17 +794,16 @@ void SeparableBoxFilterDataStore::FindNearestEntities(GeneralizedDistance &dist_ distances_out.resize(num_results); previous_nn_cache.resize(num_results); bool need_recompute_distances = (dist_params.recomputeAccurateDistances && !dist_params.highAccuracy); - if(need_recompute_distances) - dist_params.SetHighAccuracy(true); + high_accuracy = (dist_params.recomputeAccurateDistances || dist_params.highAccuracy); while(sorted_results.Size() > 0) { auto &drp = sorted_results.Top(); double distance; if(!need_recompute_distances) - distance = dist_params.InverseExponentiateDistance(drp.distance); + distance = dist_params.InverseExponentiateDistance(drp.distance, high_accuracy); else - distance = GetDistanceBetween(dist_params, target_values, target_value_types, target_column_indices, drp.reference); + distance = GetDistanceBetween(dist_params, target_values, target_value_types, target_column_indices, drp.reference, true); size_t output_index = sorted_results.Size() - 1; distances_out[output_index] = DistanceReferencePair(distance, drp.reference); @@ -862,12 +872,13 @@ double SeparableBoxFilterDataStore::PopulatePartialSumsWithSimilarFeatureValue(G { auto &column = columnData[absolute_feature_index]; auto effective_feature_type = dist_params.featureParams[query_feature_index].effectiveFeatureType; + bool high_accuracy = dist_params.highAccuracy; bool value_is_null = EvaluableNodeImmediateValue::IsNullEquivalent(value_type, value); //need to accumulate values for nulls if the value is a null if(value_is_null) { - double unknown_unknown_term = dist_params.ComputeDistanceTermUnknownToUnknown(query_feature_index); + double unknown_unknown_term = dist_params.ComputeDistanceTermUnknownToUnknown(query_feature_index, high_accuracy); AccumulatePartialSums(column->nullIndices, query_feature_index, unknown_unknown_term); AccumulatePartialSums(column->nanIndices, query_feature_index, unknown_unknown_term); @@ -875,8 +886,8 @@ double SeparableBoxFilterDataStore::PopulatePartialSumsWithSimilarFeatureValue(G // if a data set is mostly nulls, it'll be slower, but this is acceptable as a more rare situation //if the known-unknown term is less than unknown_unknown (this should be rare if nulls have semantic meaning) //then need to populate the rest of the cases - double known_unknown_term = dist_params.ComputeDistanceTermKnownToUnknown(query_feature_index); - if(effective_feature_type == GeneralizedDistance::EFDT_NOMINAL || known_unknown_term < unknown_unknown_term) + double known_unknown_term = dist_params.ComputeDistanceTermKnownToUnknown(query_feature_index, high_accuracy); + if(effective_feature_type == GeneralizedDistance::EFDT_NOMINAL_UNIVERSALLY_SYMMETRIC_PRECOMPUTED || known_unknown_term < unknown_unknown_term) { BitArrayIntegerSet &known_unknown_indices = parametersAndBuffers.potentialMatchesSet; known_unknown_indices = enabled_indices; @@ -892,20 +903,20 @@ double SeparableBoxFilterDataStore::PopulatePartialSumsWithSimilarFeatureValue(G //but if made it here, then the value itself isn't null if(dist_params.IsKnownToUnknownDistanceLessThanOrEqualToExactMatch(query_feature_index)) { - double known_unknown_term = dist_params.ComputeDistanceTermKnownToUnknown(query_feature_index); + double known_unknown_term = dist_params.ComputeDistanceTermKnownToUnknown(query_feature_index, high_accuracy); AccumulatePartialSums(column->nullIndices, query_feature_index, known_unknown_term); AccumulatePartialSums(column->nanIndices, query_feature_index, known_unknown_term); } //if nominal, only need to compute the exact match - if(effective_feature_type == GeneralizedDistance::EFDT_NOMINAL) + if(effective_feature_type == GeneralizedDistance::EFDT_NOMINAL_UNIVERSALLY_SYMMETRIC_PRECOMPUTED) { if(value_type == ENIVT_NUMBER) { auto [value_index, exact_index_found] = column->FindExactIndexForValue(value.number); if(exact_index_found) { - double term = dist_params.ComputeDistanceTermNominalExactMatch(query_feature_index); + double term = dist_params.ComputeDistanceTermNominalUniversallySymmetricExactMatchPrecomputed(query_feature_index, high_accuracy); AccumulatePartialSums(column->sortedNumberValueEntries[value_index]->indicesWithValue, query_feature_index, term); } } @@ -914,7 +925,7 @@ double SeparableBoxFilterDataStore::PopulatePartialSumsWithSimilarFeatureValue(G auto value_found = column->stringIdValueToIndices.find(value.stringID); if(value_found != end(column->stringIdValueToIndices)) { - double term = dist_params.ComputeDistanceTermNominalExactMatch(query_feature_index); + double term = dist_params.ComputeDistanceTermNominalUniversallySymmetricExactMatchPrecomputed(query_feature_index, high_accuracy); AccumulatePartialSums(*(value_found->second), query_feature_index, term); } } @@ -930,13 +941,13 @@ double SeparableBoxFilterDataStore::PopulatePartialSumsWithSimilarFeatureValue(G { auto &entity_indices = *(value_found->second); ComputeAndAccumulatePartialSums(dist_params, value, value_type, - entity_indices, query_feature_index, absolute_feature_index); + entity_indices, query_feature_index, absolute_feature_index, high_accuracy); } } //else value_type == ENIVT_NULL //didn't find the value - return dist_params.ComputeDistanceTermNominalNonMatch(query_feature_index); + return dist_params.ComputeDistanceTermNominalUniversallySymmetricNonMatchPrecomputed(query_feature_index, high_accuracy); } else if(effective_feature_type == GeneralizedDistance::EFDT_CONTINUOUS_STRING) { @@ -945,13 +956,13 @@ double SeparableBoxFilterDataStore::PopulatePartialSumsWithSimilarFeatureValue(G auto value_found = column->stringIdValueToIndices.find(value.stringID); if(value_found != end(column->stringIdValueToIndices)) { - double term = dist_params.ComputeDistanceTermNonNominalExactMatch(query_feature_index); + double term = dist_params.ComputeDistanceTermNonNominalExactMatch(query_feature_index, high_accuracy); AccumulatePartialSums(*(value_found->second), query_feature_index, term); } } //the next closest string will have an edit distance of 1 - return dist_params.ComputeDistanceTermNonNominalNonCyclicNonNullRegular(1.0, query_feature_index); + return dist_params.ComputeDistanceTermNonNominalNonCyclicNonNullRegular(1.0, query_feature_index, high_accuracy); } else if(effective_feature_type == GeneralizedDistance::EFDT_CONTINUOUS_CODE) { @@ -965,17 +976,17 @@ double SeparableBoxFilterDataStore::PopulatePartialSumsWithSimilarFeatureValue(G { auto &entity_indices = *(value_found->second); ComputeAndAccumulatePartialSums(dist_params, value, value_type, - entity_indices, query_feature_index, absolute_feature_index); + entity_indices, query_feature_index, absolute_feature_index, high_accuracy); } //next most similar code must be at least a distance of 1 edit away - return dist_params.ComputeDistanceTermNonNominalNonCyclicNonNullRegular(1.0, query_feature_index); + return dist_params.ComputeDistanceTermNonNominalNonCyclicNonNullRegular(1.0, query_feature_index, high_accuracy); } //else feature_type == FDT_CONTINUOUS_NUMERIC or FDT_CONTINUOUS_UNIVERSALLY_NUMERIC //if not a number or no numbers available, then no size if(value_type != ENIVT_NUMBER || column->sortedNumberValueEntries.size() == 0) - return GetMaxDistanceTermFromValue(dist_params, value, value_type, query_feature_index, absolute_feature_index); + return GetMaxDistanceTermFromValue(dist_params, value, value_type, query_feature_index, absolute_feature_index, high_accuracy); bool cyclic_feature = dist_params.IsFeatureCyclic(query_feature_index); double cycle_length = std::numeric_limits::infinity(); @@ -986,9 +997,10 @@ double SeparableBoxFilterDataStore::PopulatePartialSumsWithSimilarFeatureValue(G double term = 0.0; if(exact_index_found) - term = dist_params.ComputeDistanceTermNonNominalExactMatch(query_feature_index); + term = dist_params.ComputeDistanceTermNonNominalExactMatch(query_feature_index, high_accuracy); else - term = dist_params.ComputeDistanceTermNonNominalNonNullRegular(value.number - column->sortedNumberValueEntries[value_index]->value.number, query_feature_index); + term = dist_params.ComputeDistanceTermNonNominalNonNullRegular( + value.number - column->sortedNumberValueEntries[value_index]->value.number, query_feature_index, high_accuracy); size_t num_entities_computed = AccumulatePartialSums(column->sortedNumberValueEntries[value_index]->indicesWithValue, query_feature_index, term); @@ -1137,8 +1149,9 @@ double SeparableBoxFilterDataStore::PopulatePartialSumsWithSimilarFeatureValue(G break; } - term = dist_params.ComputeDistanceTermNonNominalNonNullRegular(next_closest_diff, query_feature_index); - num_entities_computed += AccumulatePartialSums(column->sortedNumberValueEntries[next_closest_index]->indicesWithValue, query_feature_index, term); + term = dist_params.ComputeDistanceTermNonNominalNonNullRegular(next_closest_diff, query_feature_index, high_accuracy); + num_entities_computed += AccumulatePartialSums( + column->sortedNumberValueEntries[next_closest_index]->indicesWithValue, query_feature_index, term); //track the rate of change of difference if(next_closest_diff - last_diff > largest_diff_delta) diff --git a/src/Amalgam/SeparableBoxFilterDataStore.h b/src/Amalgam/SeparableBoxFilterDataStore.h index 5ade6f81..2a1ace46 100644 --- a/src/Amalgam/SeparableBoxFilterDataStore.h +++ b/src/Amalgam/SeparableBoxFilterDataStore.h @@ -73,14 +73,14 @@ class SeparableBoxFilterDataStore // query_feature_index is relative to dist_params inline double GetMaxDistanceTermFromValue(GeneralizedDistance &dist_params, EvaluableNodeImmediateValue &value, EvaluableNodeImmediateValueType value_type, - size_t query_feature_index, size_t absolute_feature_index) + size_t query_feature_index, size_t absolute_feature_index, bool high_accuracy) { if(dist_params.IsFeatureNominal(query_feature_index)) - return dist_params.ComputeDistanceTermNominalNonMatch(query_feature_index); + return dist_params.ComputeDistanceTermNominalUniversallySymmetricNonMatchPrecomputed(query_feature_index, high_accuracy); double max_diff = columnData[absolute_feature_index]->GetMaxDifferenceTermFromValue( dist_params.featureParams[query_feature_index], value_type, value); - return dist_params.ComputeDistanceTermNonNominalNonNullRegular(max_diff, query_feature_index); + return dist_params.ComputeDistanceTermNonNominalNonNullRegular(max_diff, query_feature_index, high_accuracy); } //gets the matrix cell index for the specified index @@ -540,7 +540,7 @@ class SeparableBoxFilterDataStore //returns the number of entities indices accumulated size_t ComputeAndAccumulatePartialSums(GeneralizedDistance &dist_params, EvaluableNodeImmediateValue value, EvaluableNodeImmediateValueType value_type, - SortedIntegerSet &entity_indices, size_t query_feature_index, size_t absolute_feature_index) + SortedIntegerSet &entity_indices, size_t query_feature_index, size_t absolute_feature_index, bool high_accuracy) { size_t num_entity_indices = entity_indices.size(); @@ -558,7 +558,7 @@ class SeparableBoxFilterDataStore other_value_type = column_data->GetResolvedValueType(other_value_type); //compute term - double term = dist_params.ComputeDistanceTermRegular(value, other_value, value_type, other_value_type, query_feature_index); + double term = dist_params.ComputeDistanceTermRegular(value, other_value, value_type, other_value_type, query_feature_index, high_accuracy); //accumulate partial_sums.Accum(entity_index, accum_location, term); @@ -681,7 +681,7 @@ class SeparableBoxFilterDataStore //returns the distance between two nodes while respecting the feature mask inline double GetDistanceBetween(GeneralizedDistance &dist_params, std::vector &target_values, std::vector &target_value_types, - std::vector &target_column_indices, size_t other_index) + std::vector &target_column_indices, size_t other_index, bool high_accuracy) { const size_t matrix_base_position = other_index * columnData.size(); @@ -697,11 +697,12 @@ class SeparableBoxFilterDataStore auto other_value = column_data->GetResolvedValue(other_value_type, matrix[matrix_base_position + column_index]); other_value_type = column_data->GetResolvedValueType(other_value_type); - dist_accum += dist_params.ComputeDistanceTermRegular(target_values[i], other_value, target_value_types[i], other_value_type, i); + dist_accum += dist_params.ComputeDistanceTermRegular( + target_values[i], other_value, target_value_types[i], other_value_type, i, high_accuracy); } } - double dist = dist_params.InverseExponentiateDistance(dist_accum); + double dist = dist_params.InverseExponentiateDistance(dist_accum, high_accuracy); return dist; } @@ -710,23 +711,26 @@ class SeparableBoxFilterDataStore //assumes that null values have already been taken care of for nominals __forceinline double ComputeDistanceTermNonMatch(GeneralizedDistance &dist_params, std::vector &target_label_indices, std::vector &target_values, std::vector &target_value_types, - size_t entity_index, size_t query_feature_index) + size_t entity_index, size_t query_feature_index, bool high_accuracy) { switch(dist_params.featureParams[query_feature_index].effectiveFeatureType) { - case GeneralizedDistance::EFDT_NOMINAL: - return dist_params.ComputeDistanceTermNominalNonMatch(query_feature_index); + case GeneralizedDistance::EFDT_NOMINAL_UNIVERSALLY_SYMMETRIC_PRECOMPUTED: + return dist_params.ComputeDistanceTermNominalUniversallySymmetricNonMatchPrecomputed(query_feature_index, high_accuracy); case GeneralizedDistance::EFDT_CONTINUOUS_UNIVERSALLY_NUMERIC: { const size_t column_index = target_label_indices[query_feature_index]; - return dist_params.ComputeDistanceTermNonNominalNonCyclicOneNonNullRegular(target_values[query_feature_index].number - GetValue(entity_index, column_index).number, query_feature_index); + return dist_params.ComputeDistanceTermNonNominalNonCyclicOneNonNullRegular( + target_values[query_feature_index].number - GetValue(entity_index, column_index).number, + query_feature_index, high_accuracy); } case GeneralizedDistance::EFDT_VALUES_UNIVERSALLY_PRECOMPUTED: { const size_t column_index = target_label_indices[query_feature_index]; - return dist_params.ComputeDistanceTermNumberInterned(GetValue(entity_index, column_index).indirectionIndex, query_feature_index); + return dist_params.ComputeDistanceTermNumberInternedPrecomputed( + GetValue(entity_index, column_index).indirectionIndex, query_feature_index, high_accuracy); } case GeneralizedDistance::EFDT_CONTINUOUS_NUMERIC: @@ -734,9 +738,11 @@ class SeparableBoxFilterDataStore const size_t column_index = target_label_indices[query_feature_index]; auto &column_data = columnData[column_index]; if(column_data->numberIndices.contains(entity_index)) - return dist_params.ComputeDistanceTermNonNominalNonCyclicOneNonNullRegular(target_values[query_feature_index].number - GetValue(entity_index, column_index).number, query_feature_index); + return dist_params.ComputeDistanceTermNonNominalNonCyclicOneNonNullRegular( + target_values[query_feature_index].number - GetValue(entity_index, column_index).number, + query_feature_index, high_accuracy); else - return dist_params.ComputeDistanceTermKnownToUnknown(query_feature_index); + return dist_params.ComputeDistanceTermKnownToUnknown(query_feature_index, high_accuracy); } case GeneralizedDistance::EFDT_CONTINUOUS_NUMERIC_CYCLIC: @@ -744,9 +750,11 @@ class SeparableBoxFilterDataStore const size_t column_index = target_label_indices[query_feature_index]; auto &column_data = columnData[column_index]; if(column_data->numberIndices.contains(entity_index)) - return dist_params.ComputeDistanceTermNonNominalOneNonNullRegular(target_values[query_feature_index].number - GetValue(entity_index, column_index).number, query_feature_index); + return dist_params.ComputeDistanceTermNonNominalOneNonNullRegular( + target_values[query_feature_index].number - GetValue(entity_index, column_index).number, + query_feature_index, high_accuracy); else - return dist_params.ComputeDistanceTermKnownToUnknown(query_feature_index); + return dist_params.ComputeDistanceTermKnownToUnknown(query_feature_index, high_accuracy); } case GeneralizedDistance::EFDT_CONTINUOUS_NUMERIC_PRECOMPUTED: @@ -754,9 +762,10 @@ class SeparableBoxFilterDataStore const size_t column_index = target_label_indices[query_feature_index]; auto &column_data = columnData[column_index]; if(column_data->numberIndices.contains(entity_index)) - return dist_params.ComputeDistanceTermNumberInterned(GetValue(entity_index, column_index).indirectionIndex, query_feature_index); + return dist_params.ComputeDistanceTermNumberInternedPrecomputed( + GetValue(entity_index, column_index).indirectionIndex, query_feature_index, high_accuracy); else - return dist_params.ComputeDistanceTermKnownToUnknown(query_feature_index); + return dist_params.ComputeDistanceTermKnownToUnknown(query_feature_index, high_accuracy); } default: //GeneralizedDistance::EFDT_CONTINUOUS_STRING or GeneralizedDistance::EFDT_CONTINUOUS_CODE @@ -766,7 +775,9 @@ class SeparableBoxFilterDataStore auto other_value_type = column_data->GetIndexValueType(entity_index); auto other_value = column_data->GetResolvedValue(other_value_type, GetValue(entity_index, column_index)); - return dist_params.ComputeDistanceTermRegular(target_values[query_feature_index], other_value, target_value_types[query_feature_index], other_value_type, query_feature_index); + return dist_params.ComputeDistanceTermRegular( + target_values[query_feature_index], other_value, target_value_types[query_feature_index], other_value_type, + query_feature_index, high_accuracy); } } } @@ -777,7 +788,7 @@ class SeparableBoxFilterDataStore //assumes that all features that are exact matches have already been computed __forceinline double ResolveDistanceToNonMatchTargetValues(GeneralizedDistance &dist_params, std::vector &target_label_indices, std::vector &target_values, std::vector &target_value_types, - PartialSumCollection &partial_sums, size_t entity_index, size_t num_target_labels) + PartialSumCollection &partial_sums, size_t entity_index, size_t num_target_labels, bool high_accuracy) { //calculate full non-exponentiated Minkowski distance to the target double distance = partial_sums.GetSum(entity_index); @@ -789,7 +800,7 @@ class SeparableBoxFilterDataStore size_t query_feature_index = *it; distance += ComputeDistanceTermNonMatch(dist_params, target_label_indices, target_values, target_value_types, - entity_index, query_feature_index); + entity_index, query_feature_index, high_accuracy); } return distance; @@ -804,7 +815,7 @@ class SeparableBoxFilterDataStore __forceinline std::pair ResolveDistanceToNonMatchTargetValues(GeneralizedDistance &dist_params, std::vector &target_label_indices, std::vector &target_values, std::vector &target_value_types, PartialSumCollection &partial_sums, size_t entity_index, std::vector &min_distance_by_unpopulated_count, size_t num_features, - double reject_distance, std::vector &min_unpopulated_distances) + double reject_distance, std::vector &min_unpopulated_distances, bool high_accuracy) { auto [num_calculated_features, distance] = partial_sums.GetNumFilledAndSum(entity_index); @@ -831,7 +842,7 @@ class SeparableBoxFilterDataStore const size_t query_feature_index = *it; distance += ComputeDistanceTermNonMatch(dist_params, target_label_indices, target_values, target_value_types, - entity_index, query_feature_index); + entity_index, query_feature_index, high_accuracy); //break out of the loop before the iterator is incremented to save a few cycles //do this via logic to minimize the number of branches @@ -864,7 +875,7 @@ class SeparableBoxFilterDataStore target_value_types.push_back(position_value_type); if(feature_type == GeneralizedDistance::FDT_NOMINAL) - effective_feature_type = GeneralizedDistance::EFDT_NOMINAL; + effective_feature_type = GeneralizedDistance::EFDT_NOMINAL_UNIVERSALLY_SYMMETRIC_PRECOMPUTED; else if(feature_type == GeneralizedDistance::FDT_CONTINUOUS_STRING) effective_feature_type = GeneralizedDistance::EFDT_CONTINUOUS_STRING; else if(feature_type == GeneralizedDistance::FDT_CONTINUOUS_CODE) @@ -978,11 +989,11 @@ class SeparableBoxFilterDataStore sorted_results.clear(); sorted_results.SetStream(rand_stream); - dist_params.SetHighAccuracy(dist_params.highAccuracy || dist_params.recomputeAccurateDistances); + bool high_accuracy = (dist_params.highAccuracy || dist_params.recomputeAccurateDistances); for(auto index : valid_indices) { - double distance = GetDistanceBetween(dist_params, target_values, target_value_types, target_column_indices, index); + double distance = GetDistanceBetween(dist_params, target_values, target_value_types, target_column_indices, index, high_accuracy); distances_out.emplace_back(distance, index); } diff --git a/src/Amalgam/entity/EntityQueries.cpp b/src/Amalgam/entity/EntityQueries.cpp index 259363df..0eb5a34e 100644 --- a/src/Amalgam/entity/EntityQueries.cpp +++ b/src/Amalgam/entity/EntityQueries.cpp @@ -207,7 +207,7 @@ bool EntityQueryCondition::DoesEntityMatchCondition(Entity *e) radius = value; } - double distance = distParams.ComputeMinkowskiDistance(position, position_types, valueToCompare, valueTypes); + double distance = distParams.ComputeMinkowskiDistance(position, position_types, valueToCompare, valueTypes, distParams.highAccuracy); if(distance - radius > maxDistance) return false; @@ -232,7 +232,7 @@ bool EntityQueryCondition::DoesEntityMatchCondition(Entity *e) return false; } -double EntityQueryCondition::GetConditionDistanceMeasure(Entity *e) +double EntityQueryCondition::GetConditionDistanceMeasure(Entity *e, bool high_accuracy) { if(e == nullptr) return std::numeric_limits::quiet_NaN(); @@ -258,7 +258,7 @@ double EntityQueryCondition::GetConditionDistanceMeasure(Entity *e) radius = value; } - double distance = distParams.ComputeMinkowskiDistance(position, position_types, valueToCompare, valueTypes); + double distance = distParams.ComputeMinkowskiDistance(position, position_types, valueToCompare, valueTypes, high_accuracy); return distance - radius; } @@ -701,7 +701,7 @@ EvaluableNodeReference EntityQueryCondition::GetMatchingEntities(Entity *contain StochasticTieBreakingPriorityQueue> nearest_entities(randomStream.CreateOtherStreamViaRand()); for(size_t i = 0; i < matching_entities.size(); i++) { - double value = GetConditionDistanceMeasure(matching_entities[i]); + double value = GetConditionDistanceMeasure(matching_entities[i], distParams.highAccuracy); if(FastIsNaN(value)) continue; @@ -730,18 +730,11 @@ EvaluableNodeReference EntityQueryCondition::GetMatchingEntities(Entity *contain if(enm == nullptr) return EvaluableNodeReference::Null(); - if(distParams.recomputeAccurateDistances) + if(!distParams.highAccuracy && distParams.recomputeAccurateDistances) { - //store state for reversion and overwrite with compute accurate distances - bool old_recalculate_distances_accurately_state = distParams.highAccuracy; - distParams.SetHighAccuracy(true); - //recompute distance accurately for each found entity result for(auto &it : entity_values) - it.distance = GetConditionDistanceMeasure(it.reference); - - //revert to original state - distParams.SetHighAccuracy(old_recalculate_distances_accurately_state); + it.distance = GetConditionDistanceMeasure(it.reference, true); } //transform distances as appropriate @@ -775,10 +768,11 @@ EvaluableNodeReference EntityQueryCondition::GetMatchingEntities(Entity *contain //compute distances //Note that this recalculates the distance. Since this is a small number of cases, it shouldn't be a big performance impact -- for larger queries, it will use faster methods // if this becomes a performance issue, then DoesEntityMatchCondition can be refactored to optionally return the values it computed + bool high_accuracy = (distParams.highAccuracy || distParams.recomputeAccurateDistances); std::vector> entity_values; entity_values.reserve(matching_entities.size()); for(size_t i = 0; i < matching_entities.size(); i++) - entity_values.push_back(DistanceReferencePair(GetConditionDistanceMeasure(matching_entities[i]), matching_entities[i])); + entity_values.push_back(DistanceReferencePair(GetConditionDistanceMeasure(matching_entities[i], high_accuracy), matching_entities[i])); //transform distances as appropriate EntityQueriesStatistics::DistanceTransform distance_transform(transformSuprisalToProb, diff --git a/src/Amalgam/entity/EntityQueries.h b/src/Amalgam/entity/EntityQueries.h index 87946e3a..524e2597 100644 --- a/src/Amalgam/entity/EntityQueries.h +++ b/src/Amalgam/entity/EntityQueries.h @@ -32,7 +32,7 @@ class EntityQueryCondition //computes the distance measure of the condition // returns NaN if invalid - double GetConditionDistanceMeasure(Entity *e); + double GetConditionDistanceMeasure(Entity *e, bool high_accuracy); EvaluableNodeReference GetMatchingEntities(Entity *container, std::vector &matching_entities, bool from_all_entities, EvaluableNodeManager *enm); diff --git a/src/Amalgam/interpreter/InterpreterOpcodesMath.cpp b/src/Amalgam/interpreter/InterpreterOpcodesMath.cpp index 8a897fe8..e343e91a 100644 --- a/src/Amalgam/interpreter/InterpreterOpcodesMath.cpp +++ b/src/Amalgam/interpreter/InterpreterOpcodesMath.cpp @@ -1109,7 +1109,7 @@ EvaluableNodeReference Interpreter::InterpretNode_ENT_GENERALIZED_DISTANCE(Evalu dist_params.ComputeAndStoreUncertaintyDistanceTerms(i); } - double value = dist_params.ComputeMinkowskiDistance(location, location_types, origin, origin_types); + double value = dist_params.ComputeMinkowskiDistance(location, location_types, origin, origin_types, true); //free these after computation in case they had any code being used/referenced in the distance evaluableNodeManager->FreeNodeTreeIfPossible(location_node); diff --git a/src/Amalgam/out.txt b/src/Amalgam/out.txt index 249653cb..b31ba7ce 100644 --- a/src/Amalgam/out.txt +++ b/src/Amalgam/out.txt @@ -1243,7 +1243,7 @@ abcdef interpreter "C:\\Users\\Chris Hazard\\Desktop\\Howso_repos\\amalgam\\x64\\MT_Release_EXE\\Amalgam.exe" raaa 2 rwww 1 - start_time 1697958069.900312 + start_time 1699158667.067612 www 1 x 12 zz 10 @@ -1286,7 +1286,7 @@ abcdef interpreter "C:\\Users\\Chris Hazard\\Desktop\\Howso_repos\\amalgam\\x64\\MT_Release_EXE\\Amalgam.exe" raaa 2 rwww 1 - start_time 1697958069.900312 + start_time 1699158667.067612 www 1 x 12 zz 10 @@ -1328,7 +1328,7 @@ abcdef interpreter "C:\\Users\\Chris Hazard\\Desktop\\Howso_repos\\amalgam\\x64\\MT_Release_EXE\\Amalgam.exe" raaa 2 rwww 1 - start_time 1697958069.900312 + start_time 1699158667.067612 www 1 x 12 zz 10 @@ -1596,7 +1596,7 @@ e: - .inf 25: (assoc a 1) -current date-time in epoch: 2023-10-22-03.01.10.1536540 +current date-time in epoch: 2023-11-05-00.31.07.3323480 2020-06-07 00:22:59 1391230800 1391230800 @@ -3500,7 +3500,7 @@ deep sets --set_entity_root_permission-- RootTest -1697958070.384504 +1699158667.574166 (true) RootTest @@ -3730,7 +3730,7 @@ hello ) ) ) - (set_entity_rand_seed new_entity "bÁ¬ò­«1L0Ñ”-I´»ÿ") + (set_entity_rand_seed new_entity "=?Õf'0Ñ”-I´»ÿ") (set_entity_rand_seed (first (create_entities @@ -3743,7 +3743,7 @@ hello ) ) ) - " ºþŽ9Öí8­ÕV­:oàÿ" + "I<,ŽÒɪØ(¹ßß‚ÿ" ) (set_entity_rand_seed (first @@ -3779,7 +3779,7 @@ hello ) ) ) - (set_entity_rand_seed new_entity "bÁ¬ò­«1L0Ñ”-I´»ÿ") + (set_entity_rand_seed new_entity "=?Õf'0Ñ”-I´»ÿ") (set_entity_rand_seed (first (create_entities @@ -4219,11 +4219,11 @@ assoc-based: (list "Child2" "Child7") --compute_entity_convictions-- case convictions: (assoc - entity1 2.032368048313694 - entity2 0.6545062562638365 - entity3 0.8110491600614946 - entity4 1.921496391371212 - entity5 0.8151983966326795 + entity1 1.8849070781661639 + entity2 0.6863118922121982 + entity3 0.8222276770849821 + entity4 1.9468118839925106 + entity5 0.7797040417153973 ) case convictions: (assoc @@ -4723,4 +4723,4 @@ Expecting 1000: 1000 concurrent entity writes successful: (true) --total execution time-- -1.1228001117706299 +1.107090950012207