[docs]classSEWWrangler:"""Handler to resolve start/end/width parameters."""
[docs]def__init__(self,ref_widths:np.ndarray,start:Optional[Union[int,np.ndarray]]=None,end:Optional[Union[int,np.ndarray]]=None,width:Optional[Union[int,np.ndarray]]=None,translate_negative:bool=True,allow_nonnarrowing:bool=False,):"""Initialize SEW parameters. Args: ref_widths: Reference widths array. start: Start positions. end: End positions. width: Widths. translate_negative: Whether to translate negative coordinates. allow_nonnarrowing: Whether to allow ranges wider than reference. """self.ref_widths=np.asarray(ref_widths,dtype=np.int32)self.length=len(ref_widths)self.allow_nonnarrowing=allow_nonnarrowingself.start=normalize_array(start,self.length)self.end=normalize_array(end,self.length)self.width=normalize_array(width,self.length)iftranslate_negative:self.start=handle_negative_coords(self.start,self.ref_widths)self.end=handle_negative_coords(self.end,self.ref_widths)ifnotallow_nonnarrowing:# validate supplied endsifnotself.end.mask.all():too_wide=(~self.end.mask)&(self.end>self.ref_widths)ifnp.any(too_wide):idx=np.where(too_wide)[0][0]raiseException(f"solving row {idx+1}: 'allow.nonnarrowing' is FALSE and "f"the supplied end ({int(self.end[idx])}) is > refwidth")
def_validate_narrowing(self,starts:np.ndarray,widths:np.ndarray)->None:"""Validate that ranges don't exceed reference width."""ifnotself.allow_nonnarrowing:ends=starts+widths-1too_wide=ends>self.ref_widthsifnp.any(too_wide):idx=np.where(too_wide)[0][0]raiseException(f"solving row {idx+1}: 'allow.nonnarrowing' is FALSE and "f"the solved end ({int(ends[idx])}) is > refwidth")
[docs]defsolve(self)->Tuple[np.ndarray,np.ndarray]:"""Resolve Start/End/Width parameters to concrete ranges. Returns: Tuple of resolved (starts, widths) ranges. """out_starts=np.ones(self.length,dtype=np.int32)out_widths=self.ref_widths.copy()ifnotself.width.mask.all():ifnp.any((~self.width.mask)&(self.width<0)):raiseException("negative values are not allowed in 'width'")ifnotself.end.mask.all():# Width and end specified# mask = (~self.width.mask) & (~self.end.mask)out_starts=self.end-self.width+1out_widths=self.width# Validate after computingself._validate_narrowing(out_starts,out_widths)elifnotself.start.mask.all():# Width and start specified# mask = (~self.width.mask) & (~self.start.mask)out_starts=self.startout_widths=self.width# Validate after computingself._validate_narrowing(out_starts,out_widths)else:# Only width specifiedout_widths=self.width# Handle start/end specificationelifnotself.start.mask.all()andnotself.end.mask.all():out_starts=self.startout_widths=self.end-self.start+1ifnp.any(out_widths<0):raiseException("ranges contain negative width")# Validate after computingself._validate_narrowing(out_starts,out_widths)# Handle only startelifnotself.start.mask.all():out_starts=self.startout_widths=self.ref_widths-(self.start-1)# Validate after computingself._validate_narrowing(out_starts,out_widths)# Handle only endelifnotself.end.mask.all():out_widths=self.end# Validate after computingself._validate_narrowing(out_starts,out_widths)returnout_starts,out_widths