I need to maintain the multi-objective optimization implementation criteris throughout the complete implementation procedures, here among the total 8 objectives 'Area',and 'PowerDissipation' are need to be minimize and the remaining 6 objectives 'SlewRate', 'Bandwidth3dB', 'GainBWProd', 'UnityGainFreq', and 'PhaseMargin' are need to be maximize.
for the first sample 4 input population of chromosomes, i got the crowd distance
Non-dominated sets: [[0, 1, 2, 3]]
Domination Count: [0, 0, 0, 0]
front_index 0
front [0, 1, 2, 3]
crowding_distance [inf, inf, inf, inf]

for the second sample 4 input population of chromosomes, the crowd distance computation was not performed,
Non-dominated sets: [[0, 1, 3]]
Domination Count: [0, 0, 1, 0]
front_index 0
front [0, 1, 3]

if there is not domination count it compute properly, if we have domination count it does not computing the crowd diatance computation 'crowding_distance = calculate_crowding_distance(front, fitness_values, objectives)'. please debug the error and provide me the updated error free code with keeping the multi-objective optimization criteria.

# Perform Pareto fronts with crowding distances and hypervolume
def pareto_sort(population, fitness_values, objectives):
    non_dominated_sets = []
    domination_counts = [0] * len(population)
    dominated_by = [[] for _ in range(len(population))]
    
    # Initial population segregation into non-dominated sets
    for i in range(len(population)):
        for j in range(len(population)):
            if i != j:
                if dominates(fitness_values[i], fitness_values[j], objectives):
                    dominated_by[j].append(i)
                elif dominates(fitness_values[j], fitness_values[i], objectives):
                    domination_counts[i] += 1
        if domination_counts[i] == 0:
            if len(non_dominated_sets) == 0:  # First front
                non_dominated_sets.append([i])
            else:  # Add to the first front if not dominated
                non_dominated_sets[0].append(i)
    
    # Iteratively finding subsequent fronts
    current_front = 0
    while current_front < len(non_dominated_sets):
        next_front = []
        for i in non_dominated_sets[current_front]:
            for j in dominated_by[i]:
                domination_counts[j] -= 1
                if domination_counts[j] == 0:
                    next_front.append(j)
        if next_front:
            non_dominated_sets.append(next_front)
        current_front += 1
    print(f"Non-dominated sets: {non_dominated_sets}")
    print(f"Domination Count: {domination_counts}")
    # Generating Pareto Data with Crowding Distances
    pareto_data = {}
    for front_index, front in enumerate(non_dominated_sets):
        print("front_index", front_index)
        print("front", front)
        crowding_distance = calculate_crowding_distance(front, fitness_values, objectives)
        print("crowding_distance", crowding_distance)
        for chromosome_index in front:
            pareto_data[chromosome_index] = {
                "front": front_index + 1,  # 1-indexed front numbers
                "front_fitness": [fitness_values[chromosome_index][obj] for obj in objectives],
                "crowding_distance": crowding_distance[chromosome_index],
                "chromosome": population[chromosome_index]
            }
    print("print X")
    # Using update_minimum_values & calculate_hypervolume
    maximum_values, minimum_values = update_minimum_values(pareto_data, objectives)
    print("maximum_values, minimum_values", maximum_values, minimum_values)
    delta = 0.1  # small positive value to ensure the reference point is feasible
    reference_point = [maximum_values[obj] + delta if obj in ['Area', 'PowerDissipation'] else minimum_values[obj] - delta for obj in objectives]
    print("reference_point", reference_point)
    hypervolume_values = calculate_hypervolume(pareto_data, reference_point)

    print(f"Non-dominated sets: {non_dominated_sets}")
    print(f"Domination Counts: {domination_counts}")
    print(f"Pareto Data: {pareto_data}")
    print(f"Hypervolume Values: {hypervolume_values}")

    return pareto_data, hypervolume_values

# Determine dominance relationship
def dominates(individual_a, individual_b, objectives):
    better_in_any = False
    for obj in objectives:
        if obj in ['Area', 'PowerDissipation']:  # Minimization
            if individual_a[obj] < individual_b[obj]:
                better_in_any = True
            elif individual_a[obj] > individual_b[obj]:
                return False
        else:  # Maximization
            if individual_a[obj] > individual_b[obj]:
                better_in_any = True
            elif individual_a[obj] < individual_b[obj]:
                return False
    return better_in_any

# Calculate crowding distance
def calculate_crowding_distance(front, fitness_values, objectives):

    crowding_distances = [0.0 for _ in front]
    if len(front) < 3:
        return [float('inf')] * len(front)  # Assign infinite for small fronts

    for obj in objectives:
        # Determine direction of comparison
        is_minimization = obj in ['Area', 'PowerDissipation']
        # Sort individuals in the front based on the objective
        sorted_front = sorted(front, key=lambda x: fitness_values[x][obj], reverse=not is_minimization)

        # Assign infinite distance to boundary individuals
        crowding_distances[sorted_front[0]] = float('inf')
        crowding_distances[sorted_front[-1]] = float('inf')

        # Ensure there’s a valid range to avoid division by zero
        norm_factor = fitness_values[sorted_front[-1]][obj] - fitness_values[sorted_front[0]][obj]
        if norm_factor == 0:
            norm_factor = 1  # Avoid division by zero by setting a default norm_factor

        # Calculate normalized crowding distance
        for i in range(1, len(front) - 1):
            prev_diff = fitness_values[sorted_front[i]][obj] - fitness_values[sorted_front[i - 1]][obj]
            next_diff = fitness_values[sorted_front[i + 1]][obj] - fitness_values[sorted_front[i]][obj]
            # Adding normalized difference to the crowding distance
            crowding_distances[sorted_front[i]] += ((next_diff + prev_diff) / norm_factor)

    return crowding_distances