In the below code i am getting the following error in the function 'check_done' within the class 'CircuitEnvironment',
    done = self.check_done(performance_metrics)
  File "RLGNN_LTSpice.py", line 136, in check_done
    lower_bound, upper_bound = self.target_metrics[idx]
TypeError: cannot unpack non-iterable numpy.int32 object

Please debug the error and update the provided code, and i also need you to modify the below functions 'calculate_reward' and 'check_done', present in the class 'CircuitEnvironment', for my new objective logic requirements as mentioned below,
new objective logic requirements:
-'raw_actions' - is my input circuit design parameter(design component values), 'performance metrics' - is my output of the corresponding circuit design parameters ['gain', 'bw', 'ugf', 'pm'].
-this code is acting as my circuit environment, present in the loop of reinforcement learning algorithm with graph neural network.
-in my reinforcement learning process, i have two separate function, one is training and another one is testing.
-in my training phase, i am selecting predefined number of target performance metrics with in the bounds 'PERFORMANCE_METRICS_TARGET_LOW' and 'PERFORMANCE_METRICS_TARGET_HIGH' and train the agent by tuning the raw_actions to achieve the respective chosen target performance metrics were reach by the circuit environment.
-once the agent gets trained for all predefined number of target performance metrics in the training pahse, then in testing phase is activated, here we choose predefined target performance metrics(which were not chosen for training phase) and test the trained agent whether the trained agent can able to achieve the target performance metrics or not, by tuning the raw actions.
-the function 'calculate_reward' code for reward generation is act as an feedback from the circuit environment whether the target is reached or not common for both training and testing phase.
-the below given function 'calculate_reward' code for reward generation is not properly implemented for my new required objectives.
-where i need to give reward/penalties whether the output meats the target or not(not based on the ranges with in the targets are reached or not)
-we need to pass the selected target metrics from the corresponding training or testing process in to the corresponding function 'step' as input to perform reward computation and to perform 'check_done' computation.
-by comparing the results of 'c_metrics'(present obtained output performance metrics for present raw action inputs) and 'p_metrics'(previous obtained output performance metrics for previous raw action inputs), if any c_metrics reach the 'target'(which is selected for present training or testing process) based on the count we need to give very high reward else if 'c_metrics' is more nearer to the target than the 'p_metrics' then we give reward else 'p_metrics' is more nearer to the target than the 'c_metrics' then we give high penalty.
- and also we need to modify the existing function 'check_done' by checking all the 'performance_metrics' were reached the target(corresponding to training or testing) then only return true.

# Define Circuit Environment
class CircuitEnvironment:
    PERFORMANCE_METRICS_TARGET_LOW = np.array([48, 30e3, 10e6, 60])
    PERFORMANCE_METRICS_TARGET_HIGH = np.array([100, 100e3, 100e6, 75])

    def __init__(self, bounds_low, bounds_high):
        # Initialization code: setup bounds
        self.bounds_low = bounds_low
        self.bounds_high = bounds_high
        # Initialize tensors as None
        self.node_features_tensor = None
        self.edge_index = None   
        self.target_metrics = None

    def step(self, raw_actions, previous_metrics):
        raw_actions = np.squeeze(raw_actions)
        performance_metrics, temp_netlist_path = run_simulation(raw_actions)
        node_features_tensor, edge_index = netlist_to_graph(temp_netlist_path)
        reward = self.calculate_reward(performance_metrics, previous_metrics)
        done = self.check_done(performance_metrics)
        self.node_features_tensor = node_features_tensor
        self.edge_index = edge_index
        return node_features_tensor, edge_index, reward, done

    def is_performance_metrics_in_target(self, metrics):
        return np.all(metrics >= self.PERFORMANCE_METRICS_TARGET_LOW) and np.all(metrics <= self.PERFORMANCE_METRICS_TARGET_HIGH)

    def calculate_reward(self, c_metrics, p_metrics):
        metrics_order = ['gain', 'bw', 'ugf', 'pm']
            
        reward = 0             
        current_metrics = np.array([c_metrics[k] for k in metrics_order])            
        performance_metrics_in_target = self.is_performance_metrics_in_target(current_metrics)
            
        if performance_metrics_in_target:
            reward += 200
            return reward    
           
        for idx, metric_name in enumerate(metrics_order):
            current_value = c_metrics[metric_name]
            previous_value = p_metrics[metric_name]
            target_low = self.PERFORMANCE_METRICS_TARGET_LOW[idx]
            target_high = self.PERFORMANCE_METRICS_TARGET_HIGH[idx]
                               
            if metric_name in ['gain', 'bw', 'ugf']:
                if current_value > previous_value:
                    if current_value > target_high:
                        reward += self.LARGE_REWARD
                    elif current_value > target_low:
                        reward += self.SMALL_REWARD
                elif previous_value < target_low and current_value > target_low:
                    reward += self.VERY_HIGH_REWARD
                elif current_value < target_low:
                    reward -= self.LARGE_PENALTY
                        
            elif metric_name == 'pm':
                if target_low <= current_value <= target_high:
                    reward += self.LARGE_REWARD
                    if previous_value < target_low or previous_value > target_high:
                        reward += self.VERY_HIGH_REWARD
                else:
                    reward -= self.LARGE_PENALTY
                    if current_value < previous_value and current_value < target_low:
                        reward -= self.PENALTY
                    elif current_value > previous_value and current_value > target_high:
                        reward -= self.PENALTY
                        
            if metric_name == 'gain' and current_value < 0:
                reward -= 100
            
        return reward

    def check_done(self, performance_metrics):
        if self.target_metrics is None:
            return False
        for idx, metric_name in enumerate(['gain', 'bw', 'ugf', 'pm']):
            lower_bound, upper_bound = self.target_metrics[idx]
            if not (lower_bound <= performance_metrics[metric_name] <= upper_bound):
                return False
        return True

# Training Function
def train(env, agent, num_episodes, max_timesteps):
    train_targets = CircuitEnvironment.generate_random_targets(num_targets=100, seed=0)
    test_targets = CircuitEnvironment.generate_random_targets(num_targets=20, seed=1)

    start_episode = 0        
      
    for episode in range(start_episode, num_episodes): 
        target = train_targets[episode % len(train_targets)]
        
        node_features_tensor, edge_index, performance_metrics = env.reset(target)
        state = (node_features_tensor, edge_index)
        
        for t in range(max_timesteps):
            action, log_prob, perf_metrics = agent.select_action(state, performance_metrics)
            next_node_features_tensor, next_edge_index, reward, done, previous_metrics = env.step(action, perf_metrics)

# Test Function
def test(env, agent, num_targets, test_targets=None):
    
    # Load test targets from saved state if not provided
    if not test_targets:
        saved_state = load_training_state()
        if saved_state and 'test_targets' in saved_state:
            test_targets = saved_state['test_targets']
        else:
            test_targets = CircuitEnvironment.generate_random_targets(num_targets=num_targets, seed=1)

    for i, target in enumerate(test_targets):
        node_features_tensor, edge_index, performance_metrics = env.reset(target)
        state = (node_features_tensor, edge_index)
        
        done = False
        while not done:
            action, log_prob = agent.select_action(state)
            next_node_features_tensor, next_edge_index, reward, done, previous_metrics = env.step(action)
            state = (next_node_features_tensor, next_edge_index)