


'''simple docstring'''


import dataclasses
import re
import string
from typing import Any, Dict, Iterator, List, Mapping, Optional, Sequence, Tuple

import numpy as np

from . import residue_constants


UpperCAmelCase_		:   Tuple							   =		Mapping[str, np.ndarray]
UpperCAmelCase_		:   List[str]							   =		Mapping[str, Any]  # Is a nested dict.
UpperCAmelCase_		:   Optional[Any]							   =		0.01





@dataclasses.dataclass(frozen=_snake_case					)
class     lowercase__  :





							'''simple docstring'''


							A_							:      np.ndarray  # [num_res, num_atom_type, 3]

							# Amino-acid type for each residue represented as an integer between 0 and
							# 20, where 20 is 'X'.
							A_							:      np.ndarray  # [num_res]

							# Binary float mask to indicate presence of a particular atom. 1.0 if an atom
							# is present and 0.0 if not. This should be used for loss masking.
							A_							:      np.ndarray  # [num_res, num_atom_type]

							# Residue index as used in PDB. It is not necessarily continuous or 0-indexed.
							A_							:      np.ndarray  # [num_res]

							# B-factors, or temperature factors, of each residue (in sq. angstroms units),
							# representing the displacement of the residue from its ground truth mean
							# value.
							A_							:      np.ndarray  # [num_res, num_atom_type]

							# Chain indices for multi-chain predictions
							A_							:      Optional[np.ndarray]      	=  None

							# Optional remark about the protein. Included as a comment in output PDB
							# files
							A_							:      Optional[str]      	=  None

							# Templates used to generate this protein (prediction-only)
							A_							:      Optional[Sequence[str]]      	=  None

							# Chain corresponding to each parent
							A_							:      Optional[Sequence[int]]      	=  None







def 			snake_case_   (  SCREAMING_SNAKE_CASE__     ):







				"""simple docstring"""





				_SCREAMING_SNAKE_CASE :       Optional[int]     		=      R"""(\[[A-Z]+\]\n)"""
				_SCREAMING_SNAKE_CASE :       List[str]     		=      [tag.strip() for tag in re.split(SCREAMING_SNAKE_CASE__							,      SCREAMING_SNAKE_CASE__     ) if len(SCREAMING_SNAKE_CASE__     ) > 0]
				_SCREAMING_SNAKE_CASE :       Iterator[Tuple[str, List[str]]]     		=      zip(tags[0::2]							,      [l.split("""\n"""     ) for l in tags[1::2]]     )

				_SCREAMING_SNAKE_CASE :       List[str]     		=      ["N", "CA", "C"]
				_SCREAMING_SNAKE_CASE :       Optional[int]     		=      None
				_SCREAMING_SNAKE_CASE :       str     		=      None
				_SCREAMING_SNAKE_CASE :       Optional[Any]     		=      None
				for g in groups:
								if "[PRIMARY]" == g[0]:
												_SCREAMING_SNAKE_CASE :       Optional[Any]     		=      g[1][0].strip()
												for i in range(len(SCREAMING_SNAKE_CASE__     )     ):
																if seq[i] not in residue_constants.restypes:
																				_SCREAMING_SNAKE_CASE :       Union[str, Any]     		=      """X"""  # FIXME: strings are immutable
												_SCREAMING_SNAKE_CASE :       Tuple     		=      np.array(
												    [residue_constants.restype_order.get(SCREAMING_SNAKE_CASE__							,      residue_constants.restype_num     ) for res_symbol in seq]     )
								elif "[TERTIARY]" == g[0]:
												_SCREAMING_SNAKE_CASE :       List[List[float]]     		=      []
												for axis in range(3     ):
																tertiary.append(list(map(SCREAMING_SNAKE_CASE__							,      g[1][axis].split()     )     )     )
												_SCREAMING_SNAKE_CASE :       Optional[Any]     		=      np.array(SCREAMING_SNAKE_CASE__     )
												_SCREAMING_SNAKE_CASE :       Any     		=      np.zeros((len(tertiary[0]     ) // 3, residue_constants.atom_type_num, 3)     ).astype(np.floataa     )
												for i, atom in enumerate(SCREAMING_SNAKE_CASE__     ):
																_SCREAMING_SNAKE_CASE :       str     		=      np.transpose(tertiary_np[:, i::3]     )
												atom_positions *= PICO_TO_ANGSTROM
								elif "[MASK]" == g[0]:
												_SCREAMING_SNAKE_CASE :       Optional[Any]     		=      np.array(list(map({"""-""": 0, """+""": 1}.get							,      g[1][0].strip()     )     )     )
												_SCREAMING_SNAKE_CASE :       int     		=      np.zeros(
												    (
												        len(SCREAMING_SNAKE_CASE__     ),
												        residue_constants.atom_type_num,
												    )     ).astype(np.floataa     )
												for i, atom in enumerate(SCREAMING_SNAKE_CASE__     ):
																_SCREAMING_SNAKE_CASE :       Dict     		=      1
												atom_mask *= mask[..., None]

				assert aatype is not None

				return Protein(
				    atom_positions=SCREAMING_SNAKE_CASE__							,      atom_mask=SCREAMING_SNAKE_CASE__							,      aatype=SCREAMING_SNAKE_CASE__							,      residue_index=np.arange(len(SCREAMING_SNAKE_CASE__     )     )							,      b_factors=SCREAMING_SNAKE_CASE__							,      )







def 			snake_case_   (  SCREAMING_SNAKE_CASE__							,      SCREAMING_SNAKE_CASE__ = 0     ):







				"""simple docstring"""





				_SCREAMING_SNAKE_CASE :       List[str]     		=      []

				_SCREAMING_SNAKE_CASE :       Dict     		=      prot.remark
				if remark is not None:
								pdb_headers.append(f"""REMARK {remark}"""     )

				_SCREAMING_SNAKE_CASE :       List[str]     		=      prot.parents
				_SCREAMING_SNAKE_CASE :       Tuple     		=      prot.parents_chain_index
				if parents is not None and parents_chain_index is not None:
								_SCREAMING_SNAKE_CASE :       Dict     		=      [p for i, p in zip(SCREAMING_SNAKE_CASE__							,      SCREAMING_SNAKE_CASE__     ) if i == chain_id]

				if parents is None or len(SCREAMING_SNAKE_CASE__     ) == 0:
								_SCREAMING_SNAKE_CASE :       Optional[Any]     		=      ["""N/A"""]

				pdb_headers.append(f"""PARENT {" ".join(SCREAMING_SNAKE_CASE__     )}"""     )

				return pdb_headers







def 			snake_case_   (  SCREAMING_SNAKE_CASE__							,      SCREAMING_SNAKE_CASE__     ):







				"""simple docstring"""





				_SCREAMING_SNAKE_CASE :       List[str]     		=      []
				_SCREAMING_SNAKE_CASE :       List[str]     		=      pdb_str.split("""\n"""     )

				_SCREAMING_SNAKE_CASE :       Any     		=      prot.remark
				if remark is not None:
								out_pdb_lines.append(f"""REMARK {remark}"""     )

				_SCREAMING_SNAKE_CASE :       List[List[str]]
				if prot.parents is not None and len(prot.parents     ) > 0:
								_SCREAMING_SNAKE_CASE :       int     		=      []
								if prot.parents_chain_index is not None:
												_SCREAMING_SNAKE_CASE :       Dict[str, List[str]]     		=      {}
												for p, i in zip(prot.parents							,      prot.parents_chain_index     ):
																parent_dict.setdefault(str(SCREAMING_SNAKE_CASE__     )							,      []     )
																parent_dict[str(SCREAMING_SNAKE_CASE__     )].append(SCREAMING_SNAKE_CASE__     )

												_SCREAMING_SNAKE_CASE :       List[str]     		=      max([int(SCREAMING_SNAKE_CASE__     ) for chain_idx in parent_dict]     )
												for i in range(max_idx + 1     ):
																_SCREAMING_SNAKE_CASE :       Optional[Any]     		=      parent_dict.get(str(SCREAMING_SNAKE_CASE__     )							,      ["""N/A"""]     )
																parents_per_chain.append(SCREAMING_SNAKE_CASE__     )
								else:
												parents_per_chain.append(list(prot.parents     )     )
				else:
								_SCREAMING_SNAKE_CASE :       Dict     		=      [["""N/A"""]]

				def make_parent_line(SCREAMING_SNAKE_CASE__     ) -> str:
								return f"""PARENT {" ".join(SCREAMING_SNAKE_CASE__     )}"""

				out_pdb_lines.append(make_parent_line(parents_per_chain[0]     )     )

				_SCREAMING_SNAKE_CASE :       List[str]     		=      0
				for i, l in enumerate(SCREAMING_SNAKE_CASE__     ):
								if "PARENT" not in l and "REMARK" not in l:
												out_pdb_lines.append(SCREAMING_SNAKE_CASE__     )
								if "TER" in l and "END" not in lines[i + 1]:
												chain_counter += 1
												if not chain_counter >= len(SCREAMING_SNAKE_CASE__     ):
																_SCREAMING_SNAKE_CASE :       Tuple     		=      parents_per_chain[chain_counter]
												else:
																_SCREAMING_SNAKE_CASE :       Optional[Any]     		=      ["""N/A"""]

												out_pdb_lines.append(make_parent_line(SCREAMING_SNAKE_CASE__     )     )

				return "\n".join(SCREAMING_SNAKE_CASE__     )







def 			snake_case_   (  SCREAMING_SNAKE_CASE__     ):







				"""simple docstring"""





				_SCREAMING_SNAKE_CASE :       List[Any]     		=      residue_constants.restypes + ["""X"""]

				def res_atoa(SCREAMING_SNAKE_CASE__     ) -> str:
								return residue_constants.restype_atoa.get(restypes[r]							,      """UNK"""     )

				_SCREAMING_SNAKE_CASE :       List[str]     		=      residue_constants.atom_types

				_SCREAMING_SNAKE_CASE :       List[str]     		=      []

				_SCREAMING_SNAKE_CASE :       List[str]     		=      prot.atom_mask
				_SCREAMING_SNAKE_CASE :       Any     		=      prot.aatype
				_SCREAMING_SNAKE_CASE :       List[str]     		=      prot.atom_positions
				_SCREAMING_SNAKE_CASE :       Union[str, Any]     		=      prot.residue_index.astype(np.intaa     )
				_SCREAMING_SNAKE_CASE :       Optional[int]     		=      prot.b_factors
				_SCREAMING_SNAKE_CASE :       List[Any]     		=      prot.chain_index

				if np.any(aatype > residue_constants.restype_num     ):
								raise ValueError("""Invalid aatypes."""     )

				_SCREAMING_SNAKE_CASE :       Union[str, Any]     		=      get_pdb_headers(SCREAMING_SNAKE_CASE__     )
				if len(SCREAMING_SNAKE_CASE__     ) > 0:
								pdb_lines.extend(SCREAMING_SNAKE_CASE__     )

				_SCREAMING_SNAKE_CASE :       Dict     		=      aatype.shape[0]
				_SCREAMING_SNAKE_CASE :       Optional[int]     		=      1
				_SCREAMING_SNAKE_CASE :       str     		=      0
				_SCREAMING_SNAKE_CASE :       int     		=      string.ascii_uppercase
				_SCREAMING_SNAKE_CASE :       Tuple     		=      None
				# Add all atom sites.
				for i in range(SCREAMING_SNAKE_CASE__     ):
								_SCREAMING_SNAKE_CASE :       List[Any]     		=      res_atoa(aatype[i]     )
								for atom_name, pos, mask, b_factor in zip(SCREAMING_SNAKE_CASE__							,      atom_positions[i]							,      atom_mask[i]							,      b_factors[i]     ):
												if mask < 0.5:
																continue

												_SCREAMING_SNAKE_CASE :       List[Any]     		=      """ATOM"""
												_SCREAMING_SNAKE_CASE :       int     		=      atom_name if len(SCREAMING_SNAKE_CASE__     ) == 4 else f""" {atom_name}"""
												_SCREAMING_SNAKE_CASE :       Union[str, Any]     		=      """"""
												_SCREAMING_SNAKE_CASE :       List[Any]     		=      """"""
												_SCREAMING_SNAKE_CASE :       str     		=      1.0_0
												_SCREAMING_SNAKE_CASE :       Optional[Any]     		=      atom_name[0]  # Protein supports only C, N, O, S, this works.
												_SCREAMING_SNAKE_CASE :       List[Any]     		=      """"""

												_SCREAMING_SNAKE_CASE :       int     		=      """A"""
												if chain_index is not None:
																_SCREAMING_SNAKE_CASE :       List[Any]     		=      chain_tags[chain_index[i]]

												# PDB is a columnar format, every space matters here!
												_SCREAMING_SNAKE_CASE :       Any     		=      (
												    f"""{record_type:<6}{atom_index:>5} {name:<4}{alt_loc:>1}"""
												    f"""{res_name_a:>3} {chain_tag:>1}"""
												    f"""{residue_index[i]:>4}{insertion_code:>1}   """
												    f"""{pos[0]:>8.3f}{pos[1]:>8.3f}{pos[2]:>8.3f}"""
												    f"""{occupancy:>6.2f}{b_factor:>6.2f}          """
												    f"""{element:>2}{charge:>2}"""
												)
												pdb_lines.append(SCREAMING_SNAKE_CASE__     )
												atom_index += 1

								_SCREAMING_SNAKE_CASE :       str     		=      i == n - 1
								if chain_index is not None:
												if i != n - 1 and chain_index[i + 1] != prev_chain_index:
																_SCREAMING_SNAKE_CASE :       Optional[int]     		=      True
																_SCREAMING_SNAKE_CASE :       Optional[Any]     		=      chain_index[i + 1]

								if should_terminate:
												# Close the chain.
												_SCREAMING_SNAKE_CASE :       Dict     		=      """TER"""
												_SCREAMING_SNAKE_CASE :       Optional[Any]     		=      (
												    f"""{chain_end:<6}{atom_index:>5}      {res_atoa(aatype[i]     ):>3} {chain_tag:>1}{residue_index[i]:>4}"""
												)
												pdb_lines.append(SCREAMING_SNAKE_CASE__     )
												atom_index += 1

												if i != n - 1:
																# "prev" is a misnomer here. This happens at the beginning of
																# each new chain.
																pdb_lines.extend(get_pdb_headers(SCREAMING_SNAKE_CASE__							,      SCREAMING_SNAKE_CASE__     )     )

				pdb_lines.append("""END"""     )
				pdb_lines.append(""""""     )
				return "\n".join(SCREAMING_SNAKE_CASE__     )







def 			snake_case_   (  SCREAMING_SNAKE_CASE__     ):







				"""simple docstring"""





				return residue_constants.STANDARD_ATOM_MASK[prot.aatype]







def 			snake_case_   (  SCREAMING_SNAKE_CASE__							,      SCREAMING_SNAKE_CASE__							,      SCREAMING_SNAKE_CASE__ = None							,      SCREAMING_SNAKE_CASE__ = None							,      SCREAMING_SNAKE_CASE__ = None							,      SCREAMING_SNAKE_CASE__ = None							,      SCREAMING_SNAKE_CASE__ = None							,      ):







				"""simple docstring"""





				return Protein(
				    aatype=features["""aatype"""]							,      atom_positions=result["""final_atom_positions"""]							,      atom_mask=result["""final_atom_mask"""]							,      residue_index=features["""residue_index"""] + 1							,      b_factors=b_factors if b_factors is not None else np.zeros_like(result["""final_atom_mask"""]     )							,      chain_index=SCREAMING_SNAKE_CASE__							,      remark=SCREAMING_SNAKE_CASE__							,      parents=SCREAMING_SNAKE_CASE__							,      parents_chain_index=SCREAMING_SNAKE_CASE__							,      )








'''simple docstring'''


from typing import Dict

import numpy as np
import torch

from . import residue_constants as rc
from .tensor_utils import tensor_tree_map, tree_map







def 			snake_case_   (  SCREAMING_SNAKE_CASE__     ):







				"""simple docstring"""





				_SCREAMING_SNAKE_CASE :       Tuple     		=      []
				_SCREAMING_SNAKE_CASE :       Dict     		=      []
				_SCREAMING_SNAKE_CASE :       str     		=      []

				for rt in rc.restypes:
								_SCREAMING_SNAKE_CASE :       Optional[int]     		=      rc.restype_name_to_atomaa_names[rc.restype_atoa[rt]]
								restype_atomaa_to_atomaa_list.append([(rc.atom_order[name] if name else 0) for name in atom_names]     )
								_SCREAMING_SNAKE_CASE :       Optional[Any]     		=      {name: i for i, name in enumerate(SCREAMING_SNAKE_CASE__     )}
								restype_atomaa_to_atomaa_list.append(
								    [(atom_name_to_idxaa[name] if name in atom_name_to_idxaa else 0) for name in rc.atom_types]     )

								restype_atomaa_mask_list.append([(1.0 if name else 0.0) for name in atom_names]     )

				# Add dummy mapping for restype 'UNK'
				restype_atomaa_to_atomaa_list.append([0] * 14     )
				restype_atomaa_to_atomaa_list.append([0] * 37     )
				restype_atomaa_mask_list.append([0.0] * 14     )

				_SCREAMING_SNAKE_CASE :       Dict     		=      torch.tensor(
				    SCREAMING_SNAKE_CASE__							,      dtype=torch.intaa							,      device=protein["""aatype"""].device							,      )
				_SCREAMING_SNAKE_CASE :       Optional[Any]     		=      torch.tensor(
				    SCREAMING_SNAKE_CASE__							,      dtype=torch.intaa							,      device=protein["""aatype"""].device							,      )
				_SCREAMING_SNAKE_CASE :       str     		=      torch.tensor(
				    SCREAMING_SNAKE_CASE__							,      dtype=torch.floataa							,      device=protein["""aatype"""].device							,      )
				_SCREAMING_SNAKE_CASE :       int     		=      protein["""aatype"""].to(torch.long     )

				# create the mapping for (residx, atom14) --> atom37, i.e. an array
				# with shape (num_res, 14) containing the atom37 indices for this protein
				_SCREAMING_SNAKE_CASE :       List[str]     		=      restype_atomaa_to_atomaa[protein_aatype]
				_SCREAMING_SNAKE_CASE :       str     		=      restype_atomaa_mask[protein_aatype]

				_SCREAMING_SNAKE_CASE :       List[Any]     		=      residx_atomaa_mask
				_SCREAMING_SNAKE_CASE :       List[str]     		=      residx_atomaa_to_atomaa.long()

				# create the gather indices for mapping back
				_SCREAMING_SNAKE_CASE :       int     		=      restype_atomaa_to_atomaa[protein_aatype]
				_SCREAMING_SNAKE_CASE :       Dict     		=      residx_atomaa_to_atomaa.long()

				# create the corresponding mask
				_SCREAMING_SNAKE_CASE :       str     		=      torch.zeros([21, 37]							,      dtype=torch.floataa							,      device=protein["""aatype"""].device     )
				for restype, restype_letter in enumerate(rc.restypes     ):
								_SCREAMING_SNAKE_CASE :       int     		=      rc.restype_atoa[restype_letter]
								_SCREAMING_SNAKE_CASE :       Dict     		=      rc.residue_atoms[restype_name]
								for atom_name in atom_names:
												_SCREAMING_SNAKE_CASE :       List[Any]     		=      rc.atom_order[atom_name]
												_SCREAMING_SNAKE_CASE :       Union[str, Any]     		=      1

				_SCREAMING_SNAKE_CASE :       int     		=      restype_atomaa_mask[protein_aatype]
				_SCREAMING_SNAKE_CASE :       int     		=      residx_atomaa_mask

				return protein







def 			snake_case_   (  SCREAMING_SNAKE_CASE__     ):







				"""simple docstring"""





				_SCREAMING_SNAKE_CASE :       Optional[Any]     		=      tree_map(lambda SCREAMING_SNAKE_CASE__     : torch.tensor(SCREAMING_SNAKE_CASE__							,      device=batch["""aatype"""].device     )							,      SCREAMING_SNAKE_CASE__							,      np.ndarray     )
				_SCREAMING_SNAKE_CASE :       Optional[Any]     		=      tensor_tree_map(lambda SCREAMING_SNAKE_CASE__     : np.array(SCREAMING_SNAKE_CASE__     )							,      make_atomaa_masks(SCREAMING_SNAKE_CASE__     )     )
				return out




