Project: s41467-025-58439-5 | Function: choose_action (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:** Implements epsilon-greedy action selection strategy for deep Q-network in cancer gene identification. Balances exploration and exploitation by either selecting the action with highest Q-value or choosing a random action based on epsilon probability, enabling effective learning of optimal gene selection policies.

**KEY TERMS:** {
  "epsilon": "exploration probability",
  "Q_value": "action-value function",
  "greedy_action": "exploitation strategy",
  "exploration": "random action selection"
}

**CONTEXT CODE:**         self.memory_actions[index,:]=self.actions_index[:]
        if len(self.actions) >= 150-self.n_step:
            self.memory_temp[index] = 0
        else:
            self.memory_temp[index] = 1
        self.memory_counter += 1
        
    def getState(self, feature_new, network_new):
        embedding = self.sess.run(self.emb_node,
                                  feed_dict={self.feature_ori: feature_new,
                                             self.net: network_new})
        s=np.sum(embedding,axis=0,keepdims=True)
        return s

    def getAction(self,  network_new,action_selold,action_index):
        i=self.actions[-1]
        action_selold.remove(i)
        action_index[i]=0
        for j in range(network_new.shape[0]):
            if self.net_ori[i][j] > 0 and j not in action_selold and j not in self.actions:
                action_selold.append(j)
                action_index[j]=1
        if len(action_selold) == 0:
            for i in self.gene_ori:
                if i not in self.actions:
                    action_selold.append(i)
                    action_index[i]=1
        return action_selold,action_index
    
    def get_train_Q(self,action_list,network,feature,s_):
        nei_list = np.dot(action_list,self.net_ori)
        node_emb_all = self.sess.run(self.emb_node, feed_dict={self.feature_ori: feature,
                                            self.net: network})
        action_sel = []                             
        for i in range(self.n_actions):
            if nei_list[i]>0 and action_list[i]==0:
                action_sel.append(i)
        action_emb = np.zeros((len(action_sel), self.embedding_size))
        for i in range(len(action_sel)):
            index = action_sel[i]
            action_emb[i, :] = node_emb_all[index, :]
        s = np.expand_dims(s_, 0)
        s_all = np.repeat(s, len(action_sel), axis=0)

        actions_value = self.sess.run(self.q_target_, feed_dict={self.s_: s_all,
                                                                self.a_: action_emb})
        actions_value = np.squeeze(actions_value)
        qt = np.max(actions_value)
        return qt

    def choose_action(self, state,action_sel,action_index):
        """
        Implements epsilon-greedy action selection strategy for deep Q-network in cancer gene identification.

        This function balances exploration and exploitation by either selecting the action with highest Q-value
        or choosing a random action based on epsilon probability, enabling effective learning of optimal gene
        selection policies. The epsilon-greedy strategy is fundamental to RL-GenRisk's exploratory potential
        and performance in discovering meaningful gene combinations.

        Args:
            state: Current state representation
            action_sel: List of available actions for selection
            action_index: Index array for action availability

        Returns:
            action_index: Selected action index for gene selection

        Paper reference: "Throughout the training, we employed the ε-greedy strategy to choose actions
        based on Q values, thus boosting RL-GenRisk's exploratory potential"

        Formula: Action = argmax(Q(s,a)) with probability (1-ε), random otherwise
        Implementation: Epsilon-greedy strategy with Q-value computation, action selection,
                       and exploration-exploitation balance for optimal gene discovery
        """
        # MASKED: Epsilon-greedy action selection for DQN-based gene identification
        raise NotImplementedError("This function is masked for testing purposes. The function implements epsilon-greedy action selection strategy for deep Q-network in cancer gene identification, balancing exploration and exploitation by either selecting the action with highest Q-value or choosing a random action based on epsilon probability, enabling effective learning of optimal gene selection policies.")

    def getQt(self, feature_new, network_new,s_):
        all_embedding = self.getEmbedding(feature_new, network_new)
        action_sel, action_emb = self.getAction(all_embedding, network_new)
        qt = -100
        if len(action_sel)!= 0:
            s = np.expand_dims(s_, 0)
            s_all = np.repeat(s, len(action_sel), axis=0)
            actions_value = self.sess.run(self.q_target_, feed_dict={self.s_: s_all,
                                                                  self.a_: action_emb})
            actions_value = np.squeeze(actions_value)
            qt = np.max(actions_value)
        return qt


    def get_reward(self, gene_num, gene_name):
        """
        Calculates reward function for reinforcement learning agent in cancer risk gene identification.

        This function evaluates the performance of selected gene sets based on statistical significance,
        coverage metrics, and biological relevance to guide the DQN agent toward identifying optimal
        cancer risk gene signatures. The reward function is crucial for RL-GenRisk's performance in
        discovering meaningful gene combinations for cancer risk prediction.

        Args:
            gene_num: Dictionary mapping genes to patient numbers
            gene_name: List of gene names for indexing

        Returns:
            tuple: (statistical_significance_score, coverage_score, biological_relevance_score)

        Paper reference: "In reinforcement learning algorithms, the design of the reward is crucial
        to the performance of the algorithm"

        Formula: Reward = f(statistical_significance, coverage, biological_relevance)
        Implementation: Multi-objective reward calculation combining gene statistics, patient coverage,
                       and biological significance weights for optimal gene set selection
        """
        # MASKED: Reward function calculation for RL-based cancer gene identification
        raise NotImplementedError("This function is masked for testing purposes. The function calculates reward function for reinforcement learning agent in cancer risk gene identification, evaluating the performance of selected gene sets based on statistical significance, coverage metrics, and biological relevance to guide the DQN agent toward identifying optimal cancer risk gene signatures.")

    def getAcc(self, actions, patients, gene_name):
        cover_num = 0
        patients_num = len(patients.keys())
        for patient in patients:
            genes = patients[patient]
            for j in actions:
                if list(gene_name)[j] in genes:
                    cover_num += 1
                    break
        return cover_num / patients_num

    def Normalized_minmax(self,feature):
        X_scaler = preprocessing.MinMaxScaler()
        X_train = copy.deepcopy(feature)
        for i in range(len(feature[0])):
            X_train[:, [i]] = X_scaler.fit_transform(feature[:, [i]])
        return X_train

    def Normalized(self,feature):
        X_scaler = preprocessing.StandardScaler()
        X_train = X_scaler.fit_transform(feature)
        return X_train

    def get_feature(self,net,actions,weights,gene_name,gene_final):
        nodes_size = net.shape[0]
        feature = np.zeros((nodes_size, 3))
        i=0
        for gene in list(gene_name):
            feature[i][0] = np.sum(net[i])
            feature[i][1] = weights[gene]
            if gene in list(gene_final.keys()):
                feature[i][2] = len(gene_final[gene])
            i=i+1


Complete the function implementation replacing "raise NotImplementedError".