Project: s41467-025-61454-1 | Function: compute_kappa (python)

system: You are an expert Python programmer implementing algorithms from scientific papers.
Complete the masked function based on the algorithm description.
Follow Python best practices and maintain consistent code style.
Return **only** the complete Python function code.

user: Complete this Python function:

**DESCRIPTION:** Computes the kappa parameter for the Equally Distributed Equivalent (EDE) distance metric using the Kolm-Pollak inequality measure. This parameter controls the inequality aversion in the optimization model.

**KEY TERMS:** {
  "aversion_parameter": "inequality aversion parameter ε",
  "distances_open_df": "current distances to open facilities",
  "kappa": "computed κ parameter for EDE calculation"
}

**CONTEXT CODE:**     
    Parameters:
    origins_df (pandas.DataFrame): DataFrame containing origin information.
    destinations_df (pandas.DataFrame) DataFrame containing destination information.
    distances_df (pandas.DataFrame) DataFrame containing distance information.
    num_to_open (int, default=None): optional input for number of desired facilities to open when using PMED, PCENT, KPL measures.
    
    
    Returns:
    tuple: Contains cleaned dataframes and lists for populations, destinations, distances, open current list, total population, and total number to ope.
    
    '''
    
    logger.info('cleaning the data')
    
    #only get populations that are >0
    origins_df = origins_df[origins_df['U7B001'] > 0]

    #distances (including the bad ones)
    distances_df = distances_df.loc[distances_df['id_orig'].isin(origins_df['id_orig'].tolist())] 
    # make sure that the destinations are in our destinations list
    distances_df = distances_df.loc[distances_df['id_dest'].isin(destinations_df['id_dest'].tolist())] 
    #get only distances above >= 0
    distances_df = distances_df[distances_df['network'] >= 0]

    #origins (list)
    origins_list=distances_df['id_orig'].unique().tolist()
    #get destinations as list with only 
    destinations_list=distances_df['id_dest'].unique().tolist()
    
    #make sure we only have the populations of "good" origins
    origins_df = origins_df[origins_df["id_orig"].isin(origins_list)]
    
    #all currently open destinations (including bad ones)
    open_current_list = destinations_df.loc[destinations_df['dest_type']== 'supermarket', 'id_dest'].unique().tolist()
    
    #definining dictionaries    
    distances_dict = dict(zip(zip(distances_df['id_orig'], distances_df['id_dest']), distances_df['network']))
    origins_dict = dict(zip(origins_df['id_orig'],origins_df['U7B001']))
    
    total_pop=sum(origins_df['U7B001'])
    
    if num_to_open:
         open_total = num_to_open + len(open_current_list)
    else: open_total= 'n/a'
    
    return total_pop, open_current_list, origins_list, destinations_list, origins_df, destinations_df, distances_df, origins_dict, distances_dict, open_total
    


def compute_kappa(open_current_list,origins_df,distances_df,aversion_parameter):
    import os
    if os.getenv('TEST_GOLDEN', 'true') == 'false':
        raise NotImplementedError('This function is masked for testing purposes.')
    '''
    Use inequalipy to compute kappa value of given parameters

    Parameters:
    open_current_list (list): list of currently open facilities
    distances_df (pandas.Dataframe): contains information about the network distance traveled from all origins to all destinations
    aversion_parameter (float): user defined aversion to inequality parameter. Larger in magnitude signifies larger penalty of inequality.

    Returns:
    kappa (float): the appropriate value of kappa given the open facilities and populations of origins.

    '''

    logger.info('computing kappa')

    #get distances for all origins to all destinations that are already open
    distances_open_df = distances_df[distances_df["id_dest"].isin(open_current_list)]
    distances_open_df = distances_open_df.loc[distances_open_df.groupby('id_orig')['network'].idxmin()]
    distances_open_df = pd.merge(distances_open_df, origins_df, on='id_orig', how='left')
    # calculate kappa using inequalipy package
    kappa = ineq.kolmpollak.calc_kappa(distances_open_df['network'].values, epsilon = aversion_parameter, weights=distances_open_df['U7B001'].values)

    return kappa
    
    
def transform_distances(distances_df,kappa):
    import os
    if os.getenv('TEST_GOLDEN', 'true') == 'false':
        raise NotImplementedError('This function is masked for testing purposes.')
    '''
    Transform distances before optimization to lessen computational burden

    Parameters:
    distances_df (pandas.DataFrame): Contains distance information
    kappa (float): kappa value to be used in the optimization model

    Returns: distances_df with additional transformed distance column and distances_transformed_dict

    '''

    logger.info('transforming distances')

    #transforming distances
    distances_df['transformed'] = distances_df.network.apply(lambda d:np.exp(-kappa*d))
    #to get all the right pairs of origins and destinations in the dict
    distances_transformed_dict = dict(zip(zip(distances_df["id_orig"], distances_df["id_dest"]), distances_df["transformed"]))

    return distances_transformed_dict
    
    
def create_pyomo_model(kappa,origins_list,destinations_list,origins_dict,distances_dict,distances_transformed_dict,open_current_list,open_total,total_pop,measure,kp_threshold=None):
    '''
    Defines pyomo model, adds appropriate variables and constraints.
     
    Parameters: 
    kappa (float) : kappa value to be used in the optimization model
    origins_list (list) : list of origins (r)
    destinations_list (list) : list of destinations (s)
    origins_dict (dict) : dictionary of origins and populations for each origin
    distances_dict (dict) : contains information about the network distance traveled from all origins to all destinations
    distances_transformed_dict (dict) : transformed network distance traveled from all origins to all destinations
    open_current_list (list) : list of currently open facilities 
    open_total (int) : number of currently open facilities + number of desired facilities to add
    total_pop (int) : total population for all residential areas
    measure (str): Defines which measure to use in model, PCENT, PMED, KPL, or KPCON.
    kp_threshold (float,default=None) : Upper bound for Kolm-Pollak value to be used in KPCON model.

    
    Returns:
    model
    '''
    
    logger.info('building model in pyomo')
    
    
    #DEFINE DIFFERENT MODEL OBJECTIVE FUNCTIONS

    #KPL Objective: Minimize the population weighted kolm-pollak distance for residential areas (r) and services (s)
    def kpl_rule(model):
        import os
        if os.getenv('TEST_GOLDEN', 'true') == 'false':
            raise NotImplementedError('This function is masked for testing purposes.')
        return sum(origins_dict[r]*distances_transformed_dict[r,s]*model.y[r,s] for r, s in distances_transformed_dict)       
        
        
    #PMED Objective: Minimize the population weighted average distance for residential areas (r) and services (s)
    def pmed_rule(model):
        return sum(origins_dict*distances_dict*model.y[r,s] for r, s in distances_dict)
            

    #PCENT Objective: Minimize the maximum distance any resident area has to travel
    def pcent_rule(model):
        return model.u
            
    def minimax_rule(model,r,s):
        return model.y[r,s]*distances_dict <= model.u


Complete the function implementation replacing "raise NotImplementedError".