Project: s41467-025-57505-2 | Function: get_edges (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:** Constructs phasing graph edges from read alignments by identifying variant connections between adjacent positions. Implements edge filtering based on read support thresholds to remove noise.

**KEY TERMS:** {
  "dedge": "edge dictionary mapping node pairs to supporting reads",
  "node1": "variant at position i",
  "node2": "variant at position i+1",
  "edge_per_position": "edges organized by genomic position",
  "nvar": "number of variant positions"
}

**CONTEXT CODE:**             main_haps_candidates,
        )

    def filter_low_support_haps(self, haps_to_assess, min_count=3, debug=False):
        """Remove haplotypes that have a low number of supporting reads"""
        reads = self.reads_original
        read_support = self.match_reads_and_haplotypes(reads, haps_to_assess)
        good_reads = read_support.unique
        filtered_ass_haps = []
        for test_hap in good_reads:
            nread = len(good_reads[test_hap])
            if nread > 0:
                filtered_ass_haps.append(test_hap)
            elif debug:
                print(f"removing low support hap {test_hap} {nread}")
        haps_to_assess = filtered_ass_haps
        while True:
            read_support = self.match_reads_and_haplotypes(reads, haps_to_assess)
            good_reads = read_support.unique
            filtered_ass_haps = []
            for test_hap in good_reads:
                nread = len(good_reads[test_hap])
                if nread >= min_count:
                    filtered_ass_haps.append(test_hap)
                elif debug:
                    print(f"removing low support hap {test_hap} {nread}")
            if len(filtered_ass_haps) == len(haps_to_assess):
                break
            else:
                haps_to_assess = filtered_ass_haps
        if debug:
            print(
                "All removed haplotypes are ",
                set(haps_to_assess) - set(filtered_ass_haps),
            )
        return filtered_ass_haps

    @staticmethod
    def get_thres(num_reads):
        """Get thresholds for pruning some noisy links"""
        min_num = min(num_reads) if num_reads != [] else 0
        remain = [a for a in num_reads if a != min_num]
        if remain != []:
            if min(remain) > 10:
                return min(5, max(2, math.floor(min(remain) / 25)))
            if min(remain) > 5:
                return 1
        return 0

    # update the edges
    def get_edges(self, debug=False):
        """
        Constructs phasing graph edges from read alignments.

        This function identifies variant connections between adjacent positions by analyzing
        read alignments and implements edge filtering based on read support thresholds to
        remove noise. It constructs the phasing graph for haplotype assembly as described
        in the Paraphase paper, enabling accurate phasing of paralogous genes using HiFi
        sequencing data.

        Args:
            debug (bool): Enable debug output for edge removal details (default: False)

        Returns:
            None: Updates instance attributes edge_per_position, next_per_node,
                 previous_per_node, and edge_info

        Raises:
            NotImplementedError: Function has been masked for testing purposes
        """
        raise NotImplementedError("This function has been masked for testing purposes")

    def summerize_edges(self):
        """Check the number of different edges starting at each position"""
        edge_per_position = self.edge_per_position
        for pos in edge_per_position:
            nhap = -1
            if (
                pos in self.edge_info
                and self.edge_info[pos] != []
                and min(self.edge_info[pos]) >= 2
                and max(self.edge_info[pos]) > 2
            ):
                dlinks = {}
                site1 = set()
                site2 = set()
                expected_site1 = set()
                expected_site2 = set()
                for node in self.nodes:
                    base, base_pos = node.split("-")
                    base_pos = int(base_pos)
                    if base_pos == pos:
                        expected_site1.add(base)
                    elif base_pos == pos + 1:
                        expected_site2.add(base)
                for node1, node2, _ in edge_per_position[pos]:
                    site1.add(node1.split("-")[0])
                    site2.add(node2.split("-")[0])
                    dlinks.setdefault(node1, set()).add(node2)
                # add some test here
                if sorted(site1) == sorted(expected_site1) and sorted(site2) == sorted(
                    expected_site2
                ):
                    if [a for a in dlinks if len(dlinks[a]) > 1] == []:
                        nhap = 2
                    else:
                        nhap = 10
            self.dnhap.setdefault(pos, nhap)
        # break the graph at the pivot pos, can make it optional
        if self.pivot_pos - 1 in self.dnhap:
            if self.dnhap[self.pivot_pos - 1] == 2:
                self.dnhap[self.pivot_pos - 1] = 10

    def get_segments(self):
        """Break graph into segments"""
        segments = {}
        dnhap = dict(sorted(self.dnhap.items()))
        dnhap_bk = copy.deepcopy(self.dnhap)
        list_keys = list(dnhap_bk.keys())
        list_values = list(dnhap_bk.values())
        # if list_values[:3] == [10, 2, 2] and list_values != [10, 2, 2, 10]:
        #    dnhap[list_keys[0]] = 2
        # if list_values[-3:] == [2, 2, 10] and list_values != [10, 2, 2, 10]:
        #    dnhap[list_keys[-1]] = 2
        for i, val in enumerate(list_values):
            if i > 0 and i < len(list_values) - 1:
                if list_values[i - 1] == 10 and val == 2 and list_values[i + 1] == 10:
                    dnhap[list_keys[i]] = 10

        lpos = [0]
        last_nhap = dnhap[0]
        for pos in dnhap:
            if pos > 0:
                nhap = dnhap[pos]
                if nhap != last_nhap:
                    segments.setdefault((min(lpos), max(lpos)), last_nhap)
                    lpos = [pos]
                    last_nhap = nhap
                else:
                    lpos.append(pos)
        segments.setdefault((min(lpos), max(lpos)), last_nhap)
        return segments

    def update_reads(self):
        """
        After pruning edges, update the corresponding sites in reads to "x"
        """
        reads = self.reads
        edge_per_position = self.edge_per_position
        new_reads = {}


Complete the function implementation replacing "raise NotImplementedError".