func interpolateWeightFor()

in pkg/scheduler/framework/plugins/clusteraffinity/types.go [215:283]


func interpolateWeightFor(cluster *clusterv1beta1.MemberCluster, property string, sortOrder placementv1beta1.PropertySortOrder, weight int32, state *pluginState) (int32, error) {
	q, err := retrievePropertyValueFrom(cluster, property)
	if err != nil {
		return 0, fmt.Errorf("failed to perform weight interpolation based on %s for cluster %s: %w", property, cluster.Name, err)
	}
	if q == nil {
		// The property is not available for the cluster.
		return 0, nil
	}

	// Read the pre-prepared min/max values from the state, calculated in the PreScore stage.
	mm, ok := state.minMaxValuesByProperty[property]
	if !ok {
		return 0, fmt.Errorf("failed to look up extremums for property %s, no state is prepared", property)
	}
	if mm.min == nil || mm.max == nil {
		// The extremums are not available; this can happen when none of the clusters support
		// the property.
		//
		// Normally this will never occur as the check before has guaranteed that at least
		// observation has been made.
		return 0, fmt.Errorf("extremums for property %s are not available, yet a reading can be found from cluster %s", property, cluster.Name)
	}
	minQ, maxQ := mm.min, mm.max

	// Cast the quantities as floats to allow ratio estimation.
	//
	// This conversion will incur precision loss, though in most cases such loss has very limited
	// impact.
	f := q.AsApproximateFloat64()
	minF := minQ.AsApproximateFloat64()
	maxF := maxQ.AsApproximateFloat64()

	// Do a sanity check to ensure correctness.
	//
	// Normally this check would never fail.
	isInvalid := (math.IsInf(minF, 0) ||
		math.IsInf(maxF, 0) ||
		minF > maxF ||
		f < minF ||
		f > maxF)
	if isInvalid {
		return 0, fmt.Errorf("cannot interpolate weight, observed value %v, observed min %v, observed max %v", f, minF, maxF)
	}

	if minF == maxF {
		// Process a corner case where the specified property is of the same value across all
		// clusters. This is not an invalid case, however, it would result in a NaN output in
		// the weight interpolation step if left unchecked (as the value is the minimum and
		// the maximum at the same time), which might lead to confusion on the user end.
		//
		// In this case, we would assign a weight of 0.
		return 0, nil
	}

	switch sortOrder {
	case placementv1beta1.Descending:
		w := ((f - minF) / (maxF - minF)) * float64(weight)
		// Round the value.
		return int32(math.Round(w)), nil
	case placementv1beta1.Ascending:
		w := (1 - (f-minF)/(maxF-minF)) * float64(weight)
		// Round the value.
		return int32(math.Round(w)), nil
	default:
		// An invalid sort order is present. Normally this should never occur.
		return 0, fmt.Errorf("cannot interpolate weight as sort order %s is invalid", sortOrder)
	}
}