Add files using upload-large-folder tool
Browse filesThis view is limited to 50 files because it contains too many changes. See raw diff
- flax_model/alphafold3/_tools/hmmer/share/man/man1/esl-alimask.1 +618 -0
- flax_model/alphafold3/_tools/hmmer/share/man/man1/hmmalign.1 +185 -0
- flax_model/alphafold3/_tools/hmmer/share/man/man1/hmmpgmd_shard.1 +162 -0
- flax_model/alphafold3/_tools/hmmer/share/man/man1/hmmstat.1 +133 -0
- flax_model/alphafold3/_tools/hmmer/share/man/man1/makehmmerdb.1 +133 -0
- flax_model/alphafold3/_tools/hmmer/share/man/man1/nhmmer.1 +687 -0
- flax_model/alphafold3/_tools/hmmer/share/man/man1/nhmmscan.1 +472 -0
- flax_model/alphafold3/data/cpp/__init__.py +0 -0
- flax_model/alphafold3/data/cpp/msa_profile_pybind.cc +79 -0
- flax_model/alphafold3/data/msa.py +348 -0
- flax_model/alphafold3/data/msa_store.py +58 -0
- flax_model/alphafold3/data/template_realign.py +160 -0
- flax_model/alphafold3/data/templates.py +978 -0
- flax_model/alphafold3/data/tools/__init__.py +0 -0
- flax_model/alphafold3/data/tools/hmmbuild.py +136 -0
- flax_model/alphafold3/data/tools/hmmsearch.py +141 -0
- flax_model/alphafold3/data/tools/jackhmmer.py +326 -0
- flax_model/alphafold3/data/tools/mmseqs.py +303 -0
- flax_model/alphafold3/data/tools/rdkit_utils.py +535 -0
- flax_model/alphafold3/data/tools/shards.py +94 -0
- flax_model/alphafold3/data/tools/subprocess_utils.py +112 -0
- flax_model/alphafold3/jax/__init__.py +0 -0
- flax_model/alphafold3/jax/attention/__init__.py +0 -0
- flax_model/alphafold3/jax/attention/attention_base.py +355 -0
- flax_model/alphafold3/jax/attention/flash_attention.py +697 -0
- flax_model/alphafold3/jax/attention/xla_attention.py +132 -0
- flax_model/alphafold3/jax/common/__init__.py +0 -0
- flax_model/alphafold3/jax/common/triton_utils.py +118 -0
- flax_model/alphafold3/jax/gated_linear_unit/block.py +118 -0
- flax_model/alphafold3/jax/gated_linear_unit/gated_linear_unit.py +116 -0
- flax_model/alphafold3/jax/gated_linear_unit/matmul_config.py +70 -0
- flax_model/alphafold3/jax/geometry/__init__.py +20 -0
- flax_model/alphafold3/test_data/miniature_databases/nt_rna_2023_02_23_clust_seq_id_90_cov_80_rep_seq__subsampled_1000.fasta +0 -0
- scripts/infer_jackhmmer.sh +53 -0
- scripts/preflight.py +89 -0
- scripts/run_alphafold.py +1194 -0
- weight/_dep/abseil-cpp/.github/ISSUE_TEMPLATE/config.yml +5 -0
- weight/_dep/abseil-cpp/.github/PULL_REQUEST_TEMPLATE.md +8 -0
- weight/_dep/abseil-cpp/BUILD.bazel +25 -0
- weight/_dep/abseil-cpp/CMake/Googletest/CMakeLists.txt.in +14 -0
- weight/_dep/abseil-cpp/CMake/Googletest/DownloadGTest.cmake +41 -0
- weight/_dep/abseil-cpp/CMake/README.md +188 -0
- weight/_dep/abseil-cpp/CMake/install_test_project/CMakeLists.txt +25 -0
- weight/_dep/abseil-cpp/CMake/install_test_project/simple.cc +32 -0
- weight/_dep/abseil-cpp/CMake/install_test_project/test.sh +112 -0
- weight/_dep/abseil-cpp/CMakeLists.txt +270 -0
- weight/_dep/abseil-cpp/CONTRIBUTING.md +141 -0
- weight/_dep/abseil-cpp/FAQ.md +167 -0
- weight/_dep/abseil-cpp/MODULE.bazel +39 -0
- weight/_dep/abseil-cpp/PrivacyInfo.xcprivacy +14 -0
flax_model/alphafold3/_tools/hmmer/share/man/man1/esl-alimask.1
ADDED
|
@@ -0,0 +1,618 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
.TH "esl\-alimask" 1 "Aug 2023" "Easel 0.49" "Easel Manual"
|
| 2 |
+
|
| 3 |
+
.SH NAME
|
| 4 |
+
esl\-alimask \- remove columns from a multiple sequence alignment
|
| 5 |
+
|
| 6 |
+
.SH SYNOPSIS
|
| 7 |
+
|
| 8 |
+
.nf
|
| 9 |
+
\fBesl\-alimask \fR[\fIoptions\fR] \fImsafile maskfile\fR
|
| 10 |
+
(remove columns based on a mask in an input file)
|
| 11 |
+
|
| 12 |
+
\fBesl\-alimask \-t \fR[\fIoptions\fR] \fImsafile coords\fR
|
| 13 |
+
(remove a contiguous set of columns at the start and end of an alignment)
|
| 14 |
+
|
| 15 |
+
\fBesl\-alimask \-g \fR[\fIoptions\fR] \fImsafile\fR
|
| 16 |
+
(remove columns based on their frequency of gaps)
|
| 17 |
+
|
| 18 |
+
\fBesl\-alimask \-p \fR[\fIoptions\fR] \fImsafile\fR
|
| 19 |
+
(remove columns based on their posterior probability annotation)
|
| 20 |
+
|
| 21 |
+
\fBesl\-alimask \-\-rf\-is\-mask \fR[\fIoptions\fR] \fImsafile\fR
|
| 22 |
+
(only remove columns that are gaps in the RF annotation)
|
| 23 |
+
|
| 24 |
+
The \fB\-g\fR and \fB\-p\fR options may be used in combination.
|
| 25 |
+
.fi
|
| 26 |
+
|
| 27 |
+
|
| 28 |
+
.SH DESCRIPTION
|
| 29 |
+
|
| 30 |
+
.B esl\-alimask
|
| 31 |
+
reads a single input alignment, removes some columns from it
|
| 32 |
+
(i.e. masks it), and outputs the masked alignment.
|
| 33 |
+
|
| 34 |
+
.PP
|
| 35 |
+
.B esl\-alimask
|
| 36 |
+
can be run in several different modes.
|
| 37 |
+
|
| 38 |
+
.PP
|
| 39 |
+
.B esl\-alimask
|
| 40 |
+
runs in "mask file mode" by default when two
|
| 41 |
+
command-line arguments (\fImsafile\fR and \fImaskfile\fR)
|
| 42 |
+
are supplied. In this mode, a bit-vector mask in the
|
| 43 |
+
.I maskfile
|
| 44 |
+
defines which columns to keep/remove. The mask is a string that may
|
| 45 |
+
only contain the characters '0' and '1'. A '0' at position x of the
|
| 46 |
+
mask indicates that column x is excluded by the mask and should be
|
| 47 |
+
removed during masking. A '1' at position x of the mask indicates
|
| 48 |
+
that column x is included by the mask and should not be removed during
|
| 49 |
+
masking. All lines in the
|
| 50 |
+
.I maskfile
|
| 51 |
+
that begin with '#' are considered comment lines and are ignored. All
|
| 52 |
+
non-whitespace characters in non-comment lines are considered to be
|
| 53 |
+
part of the mask. The length of the mask must equal either the total
|
| 54 |
+
number of columns in the (first) alignment in
|
| 55 |
+
.I msafile,
|
| 56 |
+
or the number of columns that are not gaps in the RF annotation of that
|
| 57 |
+
alignment. The latter case is only valid if
|
| 58 |
+
.I msafile
|
| 59 |
+
is in Stockholm format and contains '#=GC RF' annotation.
|
| 60 |
+
If the mask length is equal to the non-gap RF length, all gap
|
| 61 |
+
RF columns will automatically be removed.
|
| 62 |
+
|
| 63 |
+
.PP
|
| 64 |
+
.B esl\-alimask
|
| 65 |
+
runs in "truncation mode" if the
|
| 66 |
+
.B \-t
|
| 67 |
+
option is used along with two command line arguments
|
| 68 |
+
(\fImsafile\fR and \fIcoords\fR). In this mode,
|
| 69 |
+
the alignment will be truncated by removing a contiguous set of
|
| 70 |
+
columns from the beginning and end of the alignment. The second
|
| 71 |
+
command line argument is the
|
| 72 |
+
.I coords
|
| 73 |
+
string, that specifies what range of columns to keep in the
|
| 74 |
+
alignment, all columns outside of this range will be removed.
|
| 75 |
+
The
|
| 76 |
+
.I coords
|
| 77 |
+
string consists of start and end coordinates separated
|
| 78 |
+
by any nonnumeric, nonwhitespace character or characters you like; for
|
| 79 |
+
example,
|
| 80 |
+
.BR 23..100 ,
|
| 81 |
+
.BR 23/100 ,
|
| 82 |
+
or
|
| 83 |
+
.B 23\-100
|
| 84 |
+
all work. To keep all alignment columns beginning at 23 until the
|
| 85 |
+
end of the alignment, you
|
| 86 |
+
can omit the end; for example,
|
| 87 |
+
.B 23:
|
| 88 |
+
would work.
|
| 89 |
+
If the
|
| 90 |
+
.B \-\-t\-rf
|
| 91 |
+
option is used in combination with
|
| 92 |
+
.B \-t,
|
| 93 |
+
the coordinates in
|
| 94 |
+
.I coords
|
| 95 |
+
are interpreted as non-gap RF column coordinates. For example,
|
| 96 |
+
with
|
| 97 |
+
.B \-\-t\-rf,
|
| 98 |
+
a
|
| 99 |
+
.I coords
|
| 100 |
+
string of
|
| 101 |
+
.B 23\-100
|
| 102 |
+
would remove all columns before the 23rd non-gap residue in
|
| 103 |
+
the "#=GC RF" annotation and after the 100th non-gap RF residue.
|
| 104 |
+
|
| 105 |
+
.PP
|
| 106 |
+
.B esl\-alimask
|
| 107 |
+
runs in "RF mask" mode if the
|
| 108 |
+
.B \-\-rf\-is\-mask
|
| 109 |
+
option is enabled. In this mode, the alignment must be in Stockholm
|
| 110 |
+
format and contain '#=GC RF' annotation.
|
| 111 |
+
.B esl\-alimask
|
| 112 |
+
will simply remove all columns that are gaps in the RF annotation.
|
| 113 |
+
|
| 114 |
+
.PP
|
| 115 |
+
.B esl\-alimask
|
| 116 |
+
runs in "gap frequency mode" if
|
| 117 |
+
.B \-g
|
| 118 |
+
is enabled. In this mode columns for which greater than
|
| 119 |
+
.I <f>
|
| 120 |
+
fraction of the aligned sequences have gap residues will be removed.
|
| 121 |
+
By default,
|
| 122 |
+
.I <f>
|
| 123 |
+
is 0.5, but this value can be changed to
|
| 124 |
+
.I <f>
|
| 125 |
+
with the
|
| 126 |
+
.BI \-\-gapthresh " <f>"
|
| 127 |
+
option. In this mode, if the alignment is in Stockholm format and
|
| 128 |
+
has RF annotation, then all columns that are gaps in the RF annotation
|
| 129 |
+
will automatically be removed, unless
|
| 130 |
+
.B \-\-saveins
|
| 131 |
+
is enabled.
|
| 132 |
+
|
| 133 |
+
.PP
|
| 134 |
+
.B esl\-alimask
|
| 135 |
+
runs in "posterior probability mode" if
|
| 136 |
+
.B \-p
|
| 137 |
+
is enabled. In this mode, masking is based on posterior probability annotation,
|
| 138 |
+
and the input alignment must be in Stockholm format and contain '#=GR
|
| 139 |
+
PP' (posterior probability) annotation for all sequences. As a special
|
| 140 |
+
case, if
|
| 141 |
+
.B \-p
|
| 142 |
+
is used in combination with
|
| 143 |
+
.B \-\-ppcons,
|
| 144 |
+
then the input alignment need not have '#=GR PP' annotation, but must
|
| 145 |
+
contain '#=GC PP_cons' (posterior probability consensus) annotation.
|
| 146 |
+
|
| 147 |
+
.PP
|
| 148 |
+
Characters in Stockholm alignment posterior probability annotation
|
| 149 |
+
(both '#=GR PP' and '#=GC PP_cons') can have 12 possible values: the
|
| 150 |
+
ten digits '0-9', '*', and '.'. If '.', the position must correspond to
|
| 151 |
+
a gap in the sequence (for '#=GR PP') or in the RF annotation (for '#=GC
|
| 152 |
+
PP_cons'). A value of '0' indicates a posterior probability of
|
| 153 |
+
between 0.0 and 0.05, '1' indicates between 0.05 and 0.15, '2'
|
| 154 |
+
indicates between 0.15 and 0.25 and so on up to '9' which indicates
|
| 155 |
+
between 0.85 and 0.95. A value of '*' indicates a posterior
|
| 156 |
+
probability of between 0.95 and 1.0. Higher posterior probabilities
|
| 157 |
+
correspond to greater confidence that the aligned residue belongs
|
| 158 |
+
where it appears in the alignment.
|
| 159 |
+
|
| 160 |
+
.PP
|
| 161 |
+
When
|
| 162 |
+
.B \-p
|
| 163 |
+
is enabled with
|
| 164 |
+
.BI \-\-ppcons " <x>",
|
| 165 |
+
columns which have a consensus posterior probability of less than
|
| 166 |
+
.I <x>
|
| 167 |
+
will be removed during masking, and all other columns will not be removed.
|
| 168 |
+
|
| 169 |
+
.PP
|
| 170 |
+
When
|
| 171 |
+
.B \-p
|
| 172 |
+
is enabled without
|
| 173 |
+
.B \-\-ppcons,
|
| 174 |
+
the number of each possible PP value in each column is counted.
|
| 175 |
+
If
|
| 176 |
+
.I <x>
|
| 177 |
+
fraction of the sequences that contain aligned residues (i.e. do not
|
| 178 |
+
contain gaps) in a column have a posterior probability
|
| 179 |
+
greater than or equal to
|
| 180 |
+
.I <y>,
|
| 181 |
+
then that column will not be removed during masking. All columns that
|
| 182 |
+
do not meet this criterion will be removed. By default, the values of both
|
| 183 |
+
.I <x>
|
| 184 |
+
and
|
| 185 |
+
.I <y>
|
| 186 |
+
are 0.95, but they can be changed with the
|
| 187 |
+
.BI \-\-pfract " <x>"
|
| 188 |
+
and
|
| 189 |
+
.BI \-\-pthresh " <y>"
|
| 190 |
+
options, respectively.
|
| 191 |
+
|
| 192 |
+
.PP
|
| 193 |
+
In posterior probability mode, all columns that have 0 residues
|
| 194 |
+
(i.e. that are 100% gaps) will be automatically removed, unless the
|
| 195 |
+
.B \-\-pallgapok
|
| 196 |
+
option is enabled, in which case such columns will not be removed.
|
| 197 |
+
|
| 198 |
+
.PP
|
| 199 |
+
Importantly, during posterior probability masking, unless
|
| 200 |
+
.B \-\-pavg
|
| 201 |
+
is used, PP annotation
|
| 202 |
+
values are always considered to be the minimum numerical value in
|
| 203 |
+
their corresponding range. For example, a PP '9' character is converted
|
| 204 |
+
to a numerical posterior probability of 0.85. If
|
| 205 |
+
.B \-\-pavg
|
| 206 |
+
is used, PP annotation values are considered to be the average
|
| 207 |
+
numerical value in their range. For example, a PP '9' character is
|
| 208 |
+
converted to a numerical posterior probability of 0.90.
|
| 209 |
+
|
| 210 |
+
.PP
|
| 211 |
+
In posterior probability mode, if the alignment is in Stockholm format and
|
| 212 |
+
has RF annotation, then all columns that are gaps in the RF annotation
|
| 213 |
+
will automatically be removed, unless
|
| 214 |
+
.B \-\-saveins
|
| 215 |
+
is enabled.
|
| 216 |
+
|
| 217 |
+
.PP
|
| 218 |
+
A single run of
|
| 219 |
+
.B esl\-alimask
|
| 220 |
+
can perform both gap frequency-based masking and posterior
|
| 221 |
+
probability-based masking if both the
|
| 222 |
+
.B \-g
|
| 223 |
+
and
|
| 224 |
+
.B \-p
|
| 225 |
+
options are enabled. In this case, a gap frequency-based mask and a
|
| 226 |
+
posterior probability-based mask are independently computed. These
|
| 227 |
+
two masks are combined to create the final mask using a logical 'and'
|
| 228 |
+
operation. Any column that is to be removed by either the gap or PP
|
| 229 |
+
mask will be removed by the final mask.
|
| 230 |
+
|
| 231 |
+
.PP
|
| 232 |
+
With the
|
| 233 |
+
.B \-\-small
|
| 234 |
+
option,
|
| 235 |
+
.B esl\-alimask
|
| 236 |
+
will operate in memory saving mode and the required RAM for the masking
|
| 237 |
+
will be minimal (usually less than a Mb) and independent of the
|
| 238 |
+
alignment size. To use
|
| 239 |
+
.BR \-\-small ,
|
| 240 |
+
the alignment alphabet must be specified with either
|
| 241 |
+
.BR \-\-amino ,
|
| 242 |
+
.BR \-\-dna ,
|
| 243 |
+
or
|
| 244 |
+
.BR \-\-rna ,
|
| 245 |
+
and the alignment must be in Pfam format (non-interleaved, 1
|
| 246 |
+
line/sequence Stockholm format). Pfam format is the default output
|
| 247 |
+
format of INFERNAL's
|
| 248 |
+
.B cmalign
|
| 249 |
+
program. Without
|
| 250 |
+
.B \-\-small
|
| 251 |
+
the required RAM will be equal to roughly the size of the first input
|
| 252 |
+
alignment (the size of the alignment file itself if it only contains
|
| 253 |
+
one alignment).
|
| 254 |
+
|
| 255 |
+
|
| 256 |
+
.SH OUTPUT
|
| 257 |
+
|
| 258 |
+
By default,
|
| 259 |
+
.B esl\-alimask
|
| 260 |
+
will print only the masked alignment to stdout and then exit.
|
| 261 |
+
If the
|
| 262 |
+
.BI \-o " <f>"
|
| 263 |
+
option is used, the alignment will be saved to file
|
| 264 |
+
.I <f>
|
| 265 |
+
, and information on the number of columns kept and removed will be
|
| 266 |
+
printed to stdout. If
|
| 267 |
+
.B \-q
|
| 268 |
+
is used in combination with
|
| 269 |
+
.BR \-o ,
|
| 270 |
+
nothing is printed to stdout.
|
| 271 |
+
|
| 272 |
+
.PP
|
| 273 |
+
The mask(s) computed by
|
| 274 |
+
.B esl\-alimask
|
| 275 |
+
when the
|
| 276 |
+
.BR \-t ,
|
| 277 |
+
.BR \-p ,
|
| 278 |
+
.BR \-g ,
|
| 279 |
+
or
|
| 280 |
+
.B \-\-rf\-is\-mask
|
| 281 |
+
options are used can be saved to output files using the options
|
| 282 |
+
\fB\-\-fmask\-rf\fR\fI <f>\fR,
|
| 283 |
+
\fB\-\-fmask\-all\fR\fI <f>\fR,
|
| 284 |
+
\fB\-\-gmask\-rf\fR\fI <f>\fR,
|
| 285 |
+
\fB\-\-gmask\-all\fR\fI <f>\fR,
|
| 286 |
+
\fB\-\-pmask\-rf\fR\fI <f>\fR, and
|
| 287 |
+
\fB\-\-pmask\-all\fR\fI <f>\fR.
|
| 288 |
+
In all cases,
|
| 289 |
+
.I <f>
|
| 290 |
+
will contain a single line, a bit vector of length
|
| 291 |
+
.I <n>,
|
| 292 |
+
where
|
| 293 |
+
.I <n>
|
| 294 |
+
is the either the total number of columns in the alignment (for the
|
| 295 |
+
options suffixed with 'all') or the number of non-gap columns in the
|
| 296 |
+
RF annotation (for the options suffixed with 'rf'). The mask will be a
|
| 297 |
+
string of '0' and '1' characters: a '0' at position x in the mask
|
| 298 |
+
indicates column x was removed (excluded) by the mask, and a '1' at
|
| 299 |
+
position x indicates column x was kept (included) by the mask. For
|
| 300 |
+
the 'rf' suffixed options, the mask only applies to non-gap RF
|
| 301 |
+
columns. The options beginning with 'f' will save the 'final' mask
|
| 302 |
+
used to keep/remove columns from the alignment. The options beginning
|
| 303 |
+
with 'g' save the masks based on gap frequency and require
|
| 304 |
+
.BR \-g .
|
| 305 |
+
The options beginning with 'p' save the masks based on posterior
|
| 306 |
+
probabilities and require
|
| 307 |
+
.BR \-p .
|
| 308 |
+
|
| 309 |
+
|
| 310 |
+
.SH OPTIONS
|
| 311 |
+
|
| 312 |
+
.TP
|
| 313 |
+
.B \-h
|
| 314 |
+
Print brief help; includes version number and summary of
|
| 315 |
+
all options, including expert options.
|
| 316 |
+
|
| 317 |
+
.TP
|
| 318 |
+
.BI \-o " <f>"
|
| 319 |
+
Output the final, masked alignment to file
|
| 320 |
+
.I <f>
|
| 321 |
+
instead of to stdout.
|
| 322 |
+
When this option is used, information about the number of columns
|
| 323 |
+
kept/removed is printed to stdout.
|
| 324 |
+
|
| 325 |
+
.TP
|
| 326 |
+
.B \-q
|
| 327 |
+
Be quiet; do not print anything to stdout.
|
| 328 |
+
This option can only be used in combination with the
|
| 329 |
+
.B \-o
|
| 330 |
+
option.
|
| 331 |
+
|
| 332 |
+
.TP
|
| 333 |
+
.B \-\-small
|
| 334 |
+
Operate in memory saving mode. Required RAM will be independent of the
|
| 335 |
+
size of the input alignment to mask, instead of roughly the size of the
|
| 336 |
+
input alignment. When enabled, the alignment must be in
|
| 337 |
+
Pfam Stockholm (non-interleaved 1 line/seq) format (see
|
| 338 |
+
.BR esl\-reformat )
|
| 339 |
+
and the output alignment will be in Pfam format.
|
| 340 |
+
|
| 341 |
+
.TP
|
| 342 |
+
.BI \-\-informat " <s>"
|
| 343 |
+
Assert that input
|
| 344 |
+
.I msafile
|
| 345 |
+
is in alignment format
|
| 346 |
+
.IR <s> .
|
| 347 |
+
Common choices for
|
| 348 |
+
.I <s>
|
| 349 |
+
include:
|
| 350 |
+
.BR stockholm ,
|
| 351 |
+
.BR a2m ,
|
| 352 |
+
.BR afa ,
|
| 353 |
+
.BR psiblast ,
|
| 354 |
+
.BR clustal ,
|
| 355 |
+
.BR phylip .
|
| 356 |
+
For more information, and for codes for some less common formats,
|
| 357 |
+
see main documentation.
|
| 358 |
+
The string
|
| 359 |
+
.I <s>
|
| 360 |
+
is case-insensitive (\fBa2m\fR or \fBA2M\fR both work).
|
| 361 |
+
Default is
|
| 362 |
+
.B stockholm
|
| 363 |
+
format, unless
|
| 364 |
+
.B \-\-small
|
| 365 |
+
is used, in which case
|
| 366 |
+
.B pfam
|
| 367 |
+
format (non-interleaved Stockholm) is assumed.
|
| 368 |
+
|
| 369 |
+
.TP
|
| 370 |
+
.BI \-\-outformat " <s>"
|
| 371 |
+
Write the output
|
| 372 |
+
.I msafile
|
| 373 |
+
in alignment format
|
| 374 |
+
.IR <s> .
|
| 375 |
+
Common choices for
|
| 376 |
+
.I <s>
|
| 377 |
+
include:
|
| 378 |
+
.BR stockholm ,
|
| 379 |
+
.BR a2m ,
|
| 380 |
+
.BR afa ,
|
| 381 |
+
.BR psiblast ,
|
| 382 |
+
.BR clustal ,
|
| 383 |
+
.BR phylip .
|
| 384 |
+
The string
|
| 385 |
+
.I <s>
|
| 386 |
+
is case-insensitive (\fBa2m\fR or \fBA2M\fR both work).
|
| 387 |
+
Default is
|
| 388 |
+
.BR stockholm ,
|
| 389 |
+
unless
|
| 390 |
+
.B \-\-small
|
| 391 |
+
is enabled, in which case
|
| 392 |
+
.B pfam
|
| 393 |
+
(noninterleaved Stockholm) is the default output format.
|
| 394 |
+
|
| 395 |
+
|
| 396 |
+
.TP
|
| 397 |
+
.BI \-\-fmask\-rf " <f>"
|
| 398 |
+
Save the non-gap RF-length final mask used to mask the alignment
|
| 399 |
+
to file
|
| 400 |
+
.IR <f> .
|
| 401 |
+
The input alignment must be in Stockholm format and contain '#=GC RF'
|
| 402 |
+
annotation for this option to be valid. See the OUTPUT section above for
|
| 403 |
+
more details on output mask files.
|
| 404 |
+
|
| 405 |
+
.TP
|
| 406 |
+
.BI \-\-fmask\-all " <f>"
|
| 407 |
+
Save the full alignment-length final mask used to mask the alignment
|
| 408 |
+
to file
|
| 409 |
+
.IR <f> .
|
| 410 |
+
See the OUTPUT section above for more details on output mask files.
|
| 411 |
+
|
| 412 |
+
.TP
|
| 413 |
+
.B \-\-amino
|
| 414 |
+
Specify that the input alignment is a protein alignment.
|
| 415 |
+
By default,
|
| 416 |
+
.B esl\-alimask
|
| 417 |
+
will try to autodetect the alphabet, but if the alignment is
|
| 418 |
+
sufficiently small it may be ambiguous. This option defines the
|
| 419 |
+
alphabet as protein. Importantly, if
|
| 420 |
+
.B \-\-small
|
| 421 |
+
is enabled, the alphabet must be specified with either
|
| 422 |
+
.BR \-\-amino ,
|
| 423 |
+
.BR \-\-dna ,
|
| 424 |
+
or
|
| 425 |
+
.BR \-\-rna .
|
| 426 |
+
|
| 427 |
+
.TP
|
| 428 |
+
.B \-\-dna
|
| 429 |
+
Specify that the input alignment is a DNA alignment.
|
| 430 |
+
|
| 431 |
+
.TP
|
| 432 |
+
.B \-\-rna
|
| 433 |
+
Specify that the input alignment is an RNA alignment.
|
| 434 |
+
|
| 435 |
+
.TP
|
| 436 |
+
.B \-\-t\-rf
|
| 437 |
+
With
|
| 438 |
+
.BR \-t ,
|
| 439 |
+
specify that the start and end coordinates defined in
|
| 440 |
+
the second command line argument
|
| 441 |
+
.I coords
|
| 442 |
+
correspond to non-gap RF coordinates. To use this option, the
|
| 443 |
+
alignment must be in Stockholm format and have "#=GC RF"
|
| 444 |
+
annotation. See the DESCRIPTION section for an example of using the
|
| 445 |
+
.B \-\-t\-rf
|
| 446 |
+
option.
|
| 447 |
+
|
| 448 |
+
.TP
|
| 449 |
+
.B \-\-t\-rmins
|
| 450 |
+
With
|
| 451 |
+
.BR \-t ,
|
| 452 |
+
specify that all columns that are gaps in the reference (RF)
|
| 453 |
+
annotation in between the specified start and end coordinates be
|
| 454 |
+
removed. By default, these columns will be kept.
|
| 455 |
+
To use this option, the alignment must be in Stockholm format and
|
| 456 |
+
have "#=GC RF" annotation.
|
| 457 |
+
|
| 458 |
+
.TP
|
| 459 |
+
.BI \-\-gapthresh " <x>"
|
| 460 |
+
With
|
| 461 |
+
.BR \-g ,
|
| 462 |
+
specify that a column is kept (included by mask) if no more
|
| 463 |
+
than
|
| 464 |
+
.I <f>
|
| 465 |
+
fraction of sequences in the alignment have a gap ('.', '\-', or '_')
|
| 466 |
+
at that position. All other columns are removed (excluded by mask).
|
| 467 |
+
By default,
|
| 468 |
+
.I <x>
|
| 469 |
+
is 0.5.
|
| 470 |
+
|
| 471 |
+
.TP
|
| 472 |
+
.BI \-\-gmask\-rf " <f>"
|
| 473 |
+
Save the non-gap RF-length gap frequency-based mask used to mask the alignment
|
| 474 |
+
to file
|
| 475 |
+
.IR <f> .
|
| 476 |
+
The input alignment must be in Stockholm format and contain '#=GC RF'
|
| 477 |
+
annotation for this option to be valid. See the OUTPUT section above for
|
| 478 |
+
more details on output mask files.
|
| 479 |
+
|
| 480 |
+
.TP
|
| 481 |
+
.BI \-\-gmask\-all " <f>"
|
| 482 |
+
Save the full alignment-length gap frequency-based mask used to mask the alignment
|
| 483 |
+
to file
|
| 484 |
+
.IR <f> .
|
| 485 |
+
See the OUTPUT section above for more details on output mask files.
|
| 486 |
+
|
| 487 |
+
|
| 488 |
+
.TP
|
| 489 |
+
.BI \-\-pfract " <x>"
|
| 490 |
+
With
|
| 491 |
+
.BR \-p ,
|
| 492 |
+
specify that a column is kept (included by mask) if the
|
| 493 |
+
fraction of sequences with a non-gap residue in that column with a
|
| 494 |
+
posterior probability of at least
|
| 495 |
+
.I <y>
|
| 496 |
+
(from \fB\-\-pthresh\fR\fI <y>\fR) is
|
| 497 |
+
.I <x>
|
| 498 |
+
or greater. All other columns are removed (excluded by mask).
|
| 499 |
+
By default
|
| 500 |
+
.I <x>
|
| 501 |
+
is 0.95.
|
| 502 |
+
|
| 503 |
+
.TP
|
| 504 |
+
.BI \-\-pthresh " <y>"
|
| 505 |
+
With
|
| 506 |
+
.BR \-p ,
|
| 507 |
+
specify that a column is kept (included by mask) if
|
| 508 |
+
.I <x>
|
| 509 |
+
(from \fB\-\-pfract \fR\fI<x>\fR)
|
| 510 |
+
fraction of sequences with a non-gap residue in that column have a
|
| 511 |
+
posterior probability of at least
|
| 512 |
+
.IR <y> .
|
| 513 |
+
All other columns are removed (excluded by mask).
|
| 514 |
+
By default
|
| 515 |
+
.I <y>
|
| 516 |
+
is 0.95. See the DESCRIPTION section for more on
|
| 517 |
+
posterior probability (PP) masking.
|
| 518 |
+
Due to the granularity of the PP annotation, different
|
| 519 |
+
.I <y>
|
| 520 |
+
values within a range covered by a single PP character will be
|
| 521 |
+
have the same effect on masking. For example, using
|
| 522 |
+
.B \-\-pthresh 0.86
|
| 523 |
+
will have the same effect as using
|
| 524 |
+
\fB\-\-pthresh 0.94\fR.
|
| 525 |
+
|
| 526 |
+
.TP
|
| 527 |
+
.BI \-\-pavg " <x>"
|
| 528 |
+
With
|
| 529 |
+
.BR \-p ,
|
| 530 |
+
specify that a column is kept (included by mask) if
|
| 531 |
+
the average posterior probability of non-gap residues in that column
|
| 532 |
+
is at least
|
| 533 |
+
.IR <x> .
|
| 534 |
+
See the DESCRIPTION section for more on
|
| 535 |
+
posterior probability (PP) masking.
|
| 536 |
+
|
| 537 |
+
.TP
|
| 538 |
+
.BI \-\-ppcons " <x>"
|
| 539 |
+
With
|
| 540 |
+
.BR \-p ,
|
| 541 |
+
use the '#=GC PP_cons' annotation to define which columns to
|
| 542 |
+
keep/remove. A column is kept (included by mask) if the PP_cons value
|
| 543 |
+
for that column is
|
| 544 |
+
.I <x>
|
| 545 |
+
or greater. Otherwise it is removed.
|
| 546 |
+
|
| 547 |
+
.TP
|
| 548 |
+
.B \-\-pallgapok
|
| 549 |
+
With
|
| 550 |
+
.BR \-p ,
|
| 551 |
+
do not automatically remove any columns that are 100% gaps
|
| 552 |
+
(i.e. contain 0 aligned residues). By default, such columns will be
|
| 553 |
+
removed.
|
| 554 |
+
|
| 555 |
+
.TP
|
| 556 |
+
.BI \-\-pmask\-rf " <f>"
|
| 557 |
+
Save the non-gap RF-length posterior probability-based mask used to mask the alignment
|
| 558 |
+
to file
|
| 559 |
+
.IR <f> .
|
| 560 |
+
The input alignment must be in Stockholm format and contain '#=GC RF'
|
| 561 |
+
annotation for this option to be valid. See the OUTPUT section above for
|
| 562 |
+
more details on output mask files.
|
| 563 |
+
|
| 564 |
+
.TP
|
| 565 |
+
.BI \-\-pmask\-all " <f>"
|
| 566 |
+
Save the full alignment-length posterior probability-based mask used to mask the alignment
|
| 567 |
+
to file
|
| 568 |
+
.IR <f> .
|
| 569 |
+
See the OUTPUT section above for more details on output mask files.
|
| 570 |
+
|
| 571 |
+
|
| 572 |
+
.TP
|
| 573 |
+
.B \-\-keepins
|
| 574 |
+
If
|
| 575 |
+
.B \-p
|
| 576 |
+
and/or
|
| 577 |
+
.B \-g
|
| 578 |
+
is enabled and the alignment is in Stockholm or Pfam format and has '#=GC RF'
|
| 579 |
+
annotation, then allow columns that are gaps in the RF annotation to
|
| 580 |
+
possibly be kept. By default, all gap RF columns would be removed
|
| 581 |
+
automatically, but with this option enabled gap and non-gap RF columns
|
| 582 |
+
are treated identically.
|
| 583 |
+
To automatically remove all gap RF columns when using a
|
| 584 |
+
.I maskfile
|
| 585 |
+
, then define the mask in
|
| 586 |
+
.I maskfile
|
| 587 |
+
as having length equal to the non-gap RF length in the alignment.
|
| 588 |
+
To automatically remove all gap RF columns when using
|
| 589 |
+
.B \-t,
|
| 590 |
+
use the
|
| 591 |
+
.B \-\-t\-rmins
|
| 592 |
+
option.
|
| 593 |
+
|
| 594 |
+
|
| 595 |
+
|
| 596 |
+
|
| 597 |
+
|
| 598 |
+
|
| 599 |
+
|
| 600 |
+
|
| 601 |
+
.SH SEE ALSO
|
| 602 |
+
|
| 603 |
+
.nf
|
| 604 |
+
http://bioeasel.org/
|
| 605 |
+
.fi
|
| 606 |
+
|
| 607 |
+
.SH COPYRIGHT
|
| 608 |
+
|
| 609 |
+
.nf
|
| 610 |
+
Copyright (C) 2023 Howard Hughes Medical Institute.
|
| 611 |
+
Freely distributed under the BSD open source license.
|
| 612 |
+
.fi
|
| 613 |
+
|
| 614 |
+
.SH AUTHOR
|
| 615 |
+
|
| 616 |
+
.nf
|
| 617 |
+
http://eddylab.org
|
| 618 |
+
.fi
|
flax_model/alphafold3/_tools/hmmer/share/man/man1/hmmalign.1
ADDED
|
@@ -0,0 +1,185 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
.TH "hmmalign" 1 "Aug 2023" "HMMER 3.4" "HMMER Manual"
|
| 2 |
+
|
| 3 |
+
.SH NAME
|
| 4 |
+
hmmalign \- align sequences to a profile
|
| 5 |
+
|
| 6 |
+
.SH SYNOPSIS
|
| 7 |
+
.B hmmalign
|
| 8 |
+
[\fIoptions\fR]
|
| 9 |
+
.I hmmfile
|
| 10 |
+
.I seqfile
|
| 11 |
+
|
| 12 |
+
.SH DESCRIPTION
|
| 13 |
+
|
| 14 |
+
.PP
|
| 15 |
+
Perform a multiple sequence alignment of all the sequences in
|
| 16 |
+
.I seqfile
|
| 17 |
+
by aligning them individually to the profile HMM in
|
| 18 |
+
.I hmmfile.
|
| 19 |
+
The new alignment is output to stdout.
|
| 20 |
+
|
| 21 |
+
.PP
|
| 22 |
+
The
|
| 23 |
+
.I hmmfile
|
| 24 |
+
should contain only a single profile. If it contains more, only the
|
| 25 |
+
first profile in the file will be used.
|
| 26 |
+
|
| 27 |
+
.PP
|
| 28 |
+
Either
|
| 29 |
+
.I hmmfile
|
| 30 |
+
or
|
| 31 |
+
.I seqfile
|
| 32 |
+
(but not both) may be '\-' (dash), which
|
| 33 |
+
means reading this input from stdin rather than a file.
|
| 34 |
+
|
| 35 |
+
.PP
|
| 36 |
+
The sequences in
|
| 37 |
+
.I seqfile
|
| 38 |
+
are aligned in unihit local alignment mode. Therefore they should
|
| 39 |
+
already be known to contain only a single domain (or a fragment of one).
|
| 40 |
+
The optimal alignment
|
| 41 |
+
may assign some residues as nonhomologous (N and C states), in which
|
| 42 |
+
case these residues are still included in the resulting alignment, but
|
| 43 |
+
shoved to the outer edges. To trim these unaligned nonhomologous residues from
|
| 44 |
+
the result, see the
|
| 45 |
+
.B \-\-trim
|
| 46 |
+
option.
|
| 47 |
+
|
| 48 |
+
|
| 49 |
+
.SH OPTIONS
|
| 50 |
+
|
| 51 |
+
.TP
|
| 52 |
+
.B \-h
|
| 53 |
+
Help; print a brief reminder of command line usage and all available
|
| 54 |
+
options.
|
| 55 |
+
|
| 56 |
+
.TP
|
| 57 |
+
.BI \-o " <f>"
|
| 58 |
+
Direct the output alignment to file
|
| 59 |
+
.I <f>,
|
| 60 |
+
rather than to stdout.
|
| 61 |
+
|
| 62 |
+
.TP
|
| 63 |
+
.BI \-\-mapali " <f>"
|
| 64 |
+
Merge the existing alignment in file
|
| 65 |
+
.I <f>
|
| 66 |
+
into the result, where
|
| 67 |
+
.I <f>
|
| 68 |
+
is exactly the same alignment that was used to build the model in
|
| 69 |
+
.I hmmfile.
|
| 70 |
+
This is done using a map of alignment columns to consensus
|
| 71 |
+
profile positions that is stored in the
|
| 72 |
+
.I hmmfile.
|
| 73 |
+
The multiple alignment in
|
| 74 |
+
.I <f>
|
| 75 |
+
will be exactly reproduced in its consensus columns (as defined by the
|
| 76 |
+
profile), but the displayed alignment in insert columns may be
|
| 77 |
+
altered, because insertions relative to a profile are considered by
|
| 78 |
+
convention to be unaligned data.
|
| 79 |
+
|
| 80 |
+
|
| 81 |
+
.TP
|
| 82 |
+
.B \-\-trim
|
| 83 |
+
Trim nonhomologous residues (assigned to N and C states in the optimal
|
| 84 |
+
alignments) from the resulting multiple alignment output.
|
| 85 |
+
|
| 86 |
+
.TP
|
| 87 |
+
.B \-\-amino
|
| 88 |
+
Assert that sequences in
|
| 89 |
+
.I seqfile
|
| 90 |
+
are protein, bypassing alphabet autodetection.
|
| 91 |
+
|
| 92 |
+
.TP
|
| 93 |
+
.B \-\-dna
|
| 94 |
+
Assert that sequences in
|
| 95 |
+
.I seqfile
|
| 96 |
+
are DNA, bypassing alphabet autodetection.
|
| 97 |
+
|
| 98 |
+
.TP
|
| 99 |
+
.B \-\-rna
|
| 100 |
+
Assert that sequences in
|
| 101 |
+
.I seqfile
|
| 102 |
+
are RNA, bypassing alphabet autodetection.
|
| 103 |
+
|
| 104 |
+
|
| 105 |
+
.TP
|
| 106 |
+
.BI \-\-informat " <s>"
|
| 107 |
+
Assert that input
|
| 108 |
+
.I seqfile
|
| 109 |
+
is in format
|
| 110 |
+
.IR <s> ,
|
| 111 |
+
bypassing format autodetection.
|
| 112 |
+
Common choices for
|
| 113 |
+
.I <s>
|
| 114 |
+
include:
|
| 115 |
+
.BR fasta ,
|
| 116 |
+
.BR embl ,
|
| 117 |
+
.BR genbank.
|
| 118 |
+
Alignment formats also work;
|
| 119 |
+
common choices include:
|
| 120 |
+
.BR stockholm ,
|
| 121 |
+
.BR a2m ,
|
| 122 |
+
.BR afa ,
|
| 123 |
+
.BR psiblast ,
|
| 124 |
+
.BR clustal ,
|
| 125 |
+
.BR phylip .
|
| 126 |
+
For more information, and for codes for some less common formats,
|
| 127 |
+
see main documentation.
|
| 128 |
+
The string
|
| 129 |
+
.I <s>
|
| 130 |
+
is case-insensitive (\fBfasta\fR or \fBFASTA\fR both work).
|
| 131 |
+
|
| 132 |
+
.TP
|
| 133 |
+
.BI \-\-outformat " <s>"
|
| 134 |
+
Write the output alignment
|
| 135 |
+
in format
|
| 136 |
+
.IR <s> .
|
| 137 |
+
Common choices for
|
| 138 |
+
.I <s>
|
| 139 |
+
include:
|
| 140 |
+
.BR stockholm ,
|
| 141 |
+
.BR a2m ,
|
| 142 |
+
.BR afa ,
|
| 143 |
+
.BR psiblast ,
|
| 144 |
+
.BR clustal ,
|
| 145 |
+
.BR phylip .
|
| 146 |
+
The string
|
| 147 |
+
.I <s>
|
| 148 |
+
is case-insensitive (\fBa2m\fR or \fBA2M\fR both work).
|
| 149 |
+
Default is
|
| 150 |
+
.BR stockholm .
|
| 151 |
+
|
| 152 |
+
|
| 153 |
+
|
| 154 |
+
.SH SEE ALSO
|
| 155 |
+
|
| 156 |
+
See
|
| 157 |
+
.BR hmmer (1)
|
| 158 |
+
for a master man page with a list of all the individual man pages
|
| 159 |
+
for programs in the HMMER package.
|
| 160 |
+
|
| 161 |
+
.PP
|
| 162 |
+
For complete documentation, see the user guide that came with your
|
| 163 |
+
HMMER distribution (Userguide.pdf); or see the HMMER web page
|
| 164 |
+
(http://hmmer.org/).
|
| 165 |
+
|
| 166 |
+
|
| 167 |
+
|
| 168 |
+
.SH COPYRIGHT
|
| 169 |
+
|
| 170 |
+
.nf
|
| 171 |
+
Copyright (C) 2023 Howard Hughes Medical Institute.
|
| 172 |
+
Freely distributed under the BSD open source license.
|
| 173 |
+
.fi
|
| 174 |
+
|
| 175 |
+
For additional information on copyright and licensing, see the file
|
| 176 |
+
called COPYRIGHT in your HMMER source distribution, or see the HMMER
|
| 177 |
+
web page
|
| 178 |
+
(http://hmmer.org/).
|
| 179 |
+
|
| 180 |
+
|
| 181 |
+
.SH AUTHOR
|
| 182 |
+
|
| 183 |
+
.nf
|
| 184 |
+
http://eddylab.org
|
| 185 |
+
.fi
|
flax_model/alphafold3/_tools/hmmer/share/man/man1/hmmpgmd_shard.1
ADDED
|
@@ -0,0 +1,162 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
.TH "hmmpgmd_shard" 1 "Aug 2023" "HMMER 3.4" "HMMER Manual"
|
| 2 |
+
|
| 3 |
+
.SH NAME
|
| 4 |
+
hmmpgmd_shard \- sharded daemon for database search web services
|
| 5 |
+
|
| 6 |
+
|
| 7 |
+
.SH SYNOPSIS
|
| 8 |
+
.B hmmpgmd_shard
|
| 9 |
+
[\fIoptions\fR]
|
| 10 |
+
|
| 11 |
+
|
| 12 |
+
.SH DESCRIPTION
|
| 13 |
+
|
| 14 |
+
.PP
|
| 15 |
+
The
|
| 16 |
+
.B hmmpgmd_shard
|
| 17 |
+
program provides a sharded version of the
|
| 18 |
+
.B hmmpgmd
|
| 19 |
+
program that we use internally to implement high-performance HMMER services that can be accessed via the internet. See the
|
| 20 |
+
.B hmmpgmd
|
| 21 |
+
man page for a discussion of how the base
|
| 22 |
+
.B hmmpgmd
|
| 23 |
+
program is used. This man page discusses differences between
|
| 24 |
+
.B hmmpgmd_shard
|
| 25 |
+
and
|
| 26 |
+
.B hmmpgmd.
|
| 27 |
+
The base
|
| 28 |
+
.B hmmpgmd
|
| 29 |
+
program loads the entirety of its database file into RAM on every worker node, in spite of the fact that each worker node searches a predictable fraction of the database(s) contained in that file when performing searches. This wastes RAM, particularly when many worker nodes are used to accelerate searches of large databases.
|
| 30 |
+
|
| 31 |
+
.PP
|
| 32 |
+
.B Hmmpgmd_shard
|
| 33 |
+
addresses this by dividing protein sequence database files into shards. Each worker node loads only 1/Nth of the database file, where N is the number of worker nodes attached to the master. HMM database files are not sharded, meaning that every worker node will load the entire database file into RAM. Current HMM databases are much smaller than current protein sequence databases, and easily fit into the RAM of modern servers even without sharding.
|
| 34 |
+
|
| 35 |
+
.PP
|
| 36 |
+
.B Hmmpgmd_shard
|
| 37 |
+
is used in the same manner as
|
| 38 |
+
.B hmmpgmd
|
| 39 |
+
, except that it takes one additional argument:
|
| 40 |
+
.BI \-\-num_shards " <n>"
|
| 41 |
+
, which specifies the number of shards that protein databases will be divided into, and defaults to 1 if unspecified. This argument is only valid for the master node of a
|
| 42 |
+
.B hmmpgmd
|
| 43 |
+
system (i.e., when
|
| 44 |
+
.BI \-\-master
|
| 45 |
+
is passed to the
|
| 46 |
+
.B hmmpgmd
|
| 47 |
+
program), and must be equal to the number of worker nodes that will connect to the master node.
|
| 48 |
+
.B Hmmpgmd_shard
|
| 49 |
+
will signal an error if more than
|
| 50 |
+
.BI num_shards
|
| 51 |
+
worker nodes attempt to connect to the master node or if a search is started when fewer than
|
| 52 |
+
.BI num_shards
|
| 53 |
+
workers are connected to the master.
|
| 54 |
+
|
| 55 |
+
.SH OPTIONS
|
| 56 |
+
|
| 57 |
+
.TP
|
| 58 |
+
.B \-h
|
| 59 |
+
Help; print a brief reminder of command line usage and all available
|
| 60 |
+
options.
|
| 61 |
+
|
| 62 |
+
.TP
|
| 63 |
+
.BI \-\-master
|
| 64 |
+
Run as the master server.
|
| 65 |
+
|
| 66 |
+
.TP
|
| 67 |
+
.BI \-\-worker " <s>"
|
| 68 |
+
Run as a worker, connecting to the master server that is running on IP
|
| 69 |
+
address
|
| 70 |
+
.IR <s> .
|
| 71 |
+
|
| 72 |
+
.TP
|
| 73 |
+
.BI \-\-cport " <n>"
|
| 74 |
+
Port to use for communication between clients and the master server.
|
| 75 |
+
The default is 51371.
|
| 76 |
+
|
| 77 |
+
.TP
|
| 78 |
+
.BI \-\-wport " <n>"
|
| 79 |
+
Port to use for communication between workers and the master server.
|
| 80 |
+
The default is 51372.
|
| 81 |
+
|
| 82 |
+
.TP
|
| 83 |
+
.BI \-\-ccncts " <n>"
|
| 84 |
+
Maximum number of client connections to accept. The default is 16.
|
| 85 |
+
|
| 86 |
+
.TP
|
| 87 |
+
.BI \-\-wcncts " <n>"
|
| 88 |
+
Maximum number of worker connections to accept. The default is 32.
|
| 89 |
+
|
| 90 |
+
.TP
|
| 91 |
+
.BI \-\-pid " <f>"
|
| 92 |
+
Name of file into which the process id will be written.
|
| 93 |
+
|
| 94 |
+
.TP
|
| 95 |
+
.BI \-\-seqdb " <f>"
|
| 96 |
+
Name of the file (in
|
| 97 |
+
.B hmmpgmd
|
| 98 |
+
format) containing protein sequences.
|
| 99 |
+
The contents of this file will be cached for searches.
|
| 100 |
+
|
| 101 |
+
.TP
|
| 102 |
+
.BI \-\-hmmdb " <f>"
|
| 103 |
+
Name of the file containing protein HMMs. The contents of this file
|
| 104 |
+
will be cached for searches.
|
| 105 |
+
|
| 106 |
+
.TP
|
| 107 |
+
.BI \-\-cpu " <n>"
|
| 108 |
+
Number of parallel threads to use (for
|
| 109 |
+
.B \-\-worker
|
| 110 |
+
).
|
| 111 |
+
|
| 112 |
+
.TP
|
| 113 |
+
.BI \-\-num_shards " <n>"
|
| 114 |
+
Number of shards to divide cached sequence database(s) into. HMM databases are not sharded, due to their small size.
|
| 115 |
+
This option is only valid when the
|
| 116 |
+
.B \-\-master
|
| 117 |
+
option is present, and defaults to 1 if not specified.
|
| 118 |
+
.B Hmmpgmd_shard
|
| 119 |
+
requires that the number of shards be equal to the number of worker nodes, and will give errors if more than
|
| 120 |
+
.BI num_shards
|
| 121 |
+
workers attempt to connect to the master node or if a search is started with fewer than
|
| 122 |
+
.BI num_shards
|
| 123 |
+
workers connected to the master.
|
| 124 |
+
|
| 125 |
+
.SH SEE ALSO
|
| 126 |
+
|
| 127 |
+
See
|
| 128 |
+
.BR hmmmpgmd (1)
|
| 129 |
+
for a description of the base hmmpgmd command and how the daemon should be used.
|
| 130 |
+
|
| 131 |
+
.BR hmmer (1)
|
| 132 |
+
for a master man page with a list of all the individual man pages
|
| 133 |
+
for programs in the HMMER package.
|
| 134 |
+
|
| 135 |
+
.PP
|
| 136 |
+
For complete documentation, see the user guide that came with your
|
| 137 |
+
HMMER distribution (Userguide.pdf); or see the HMMER web page
|
| 138 |
+
(http://hmmer.org/).
|
| 139 |
+
|
| 140 |
+
|
| 141 |
+
|
| 142 |
+
.SH COPYRIGHT
|
| 143 |
+
|
| 144 |
+
.nf
|
| 145 |
+
Copyright (C) 2023 Howard Hughes Medical Institute.
|
| 146 |
+
Freely distributed under the BSD open source license.
|
| 147 |
+
.fi
|
| 148 |
+
|
| 149 |
+
For additional information on copyright and licensing, see the file
|
| 150 |
+
called COPYRIGHT in your HMMER source distribution, or see the HMMER
|
| 151 |
+
web page
|
| 152 |
+
(http://hmmer.org/).
|
| 153 |
+
|
| 154 |
+
|
| 155 |
+
.SH AUTHOR
|
| 156 |
+
|
| 157 |
+
.nf
|
| 158 |
+
http://eddylab.org
|
| 159 |
+
.fi
|
| 160 |
+
|
| 161 |
+
|
| 162 |
+
|
flax_model/alphafold3/_tools/hmmer/share/man/man1/hmmstat.1
ADDED
|
@@ -0,0 +1,133 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
.TH "hmmstat" 1 "Aug 2023" "HMMER 3.4" "HMMER Manual"
|
| 2 |
+
|
| 3 |
+
.SH NAME
|
| 4 |
+
hmmstat \- summary statistics for a profile file
|
| 5 |
+
|
| 6 |
+
|
| 7 |
+
.SH SYNOPSIS
|
| 8 |
+
.B hmmstat
|
| 9 |
+
[\fIoptions\fR]
|
| 10 |
+
.I hmmfile
|
| 11 |
+
|
| 12 |
+
|
| 13 |
+
.SH DESCRIPTION
|
| 14 |
+
|
| 15 |
+
The
|
| 16 |
+
.B hmmstat
|
| 17 |
+
utility prints out a tabular file of summary statistics for each
|
| 18 |
+
profile in
|
| 19 |
+
.IR hmmfile .
|
| 20 |
+
|
| 21 |
+
|
| 22 |
+
.PP
|
| 23 |
+
.I hmmfile
|
| 24 |
+
may be '\-' (a dash character), in which case profiles
|
| 25 |
+
are read from a
|
| 26 |
+
stdin
|
| 27 |
+
pipe instead of from a file.
|
| 28 |
+
|
| 29 |
+
.PP
|
| 30 |
+
The columns are:
|
| 31 |
+
|
| 32 |
+
.TP
|
| 33 |
+
.B idx
|
| 34 |
+
The index of this profile, numbering each profile in the file starting from 1.
|
| 35 |
+
|
| 36 |
+
.TP
|
| 37 |
+
.B name
|
| 38 |
+
The name of the profile.
|
| 39 |
+
|
| 40 |
+
.TP
|
| 41 |
+
.B accession
|
| 42 |
+
The optional accession of the profile, or "\-" if there is none.
|
| 43 |
+
|
| 44 |
+
.TP
|
| 45 |
+
.B nseq
|
| 46 |
+
The number of sequences that the profile was estimated from.
|
| 47 |
+
|
| 48 |
+
.TP
|
| 49 |
+
.B eff_nseq
|
| 50 |
+
The effective number of sequences that the profile was estimated
|
| 51 |
+
from, after HMMER applied an effective sequence number calculation
|
| 52 |
+
such as the default entropy weighting.
|
| 53 |
+
|
| 54 |
+
.TP
|
| 55 |
+
.B M
|
| 56 |
+
The length of the model in consensus residues (match states).
|
| 57 |
+
|
| 58 |
+
.TP
|
| 59 |
+
.B relent
|
| 60 |
+
Mean relative entropy per match state, in bits. This is the expected
|
| 61 |
+
(mean) score per consensus position. This is what the default
|
| 62 |
+
entropy-weighting method for effective sequence number estimation
|
| 63 |
+
focuses on, so for default HMMER3 models, you expect this value to
|
| 64 |
+
reflect the default target for entropy-weighting.
|
| 65 |
+
|
| 66 |
+
.TP
|
| 67 |
+
.B info
|
| 68 |
+
Mean information content per match state, in bits.
|
| 69 |
+
Probably not useful. Information content is a slightly
|
| 70 |
+
different calculation than relative entropy.
|
| 71 |
+
|
| 72 |
+
.TP
|
| 73 |
+
.B "p relE"
|
| 74 |
+
Mean positional relative entropy, in bits.
|
| 75 |
+
This is a fancier version of the per-match-state relative entropy,
|
| 76 |
+
taking into account the transition (insertion/deletion) probabilities;
|
| 77 |
+
it may be a more accurate estimation of the average score contributed
|
| 78 |
+
per model consensus position.
|
| 79 |
+
|
| 80 |
+
.TP
|
| 81 |
+
.B compKL
|
| 82 |
+
Kullback-Leibler divergence from
|
| 83 |
+
the default background frequency distribution to
|
| 84 |
+
the average composition of the profile's consensus match states, in bits.
|
| 85 |
+
The higher this number, the more biased the residue composition of the
|
| 86 |
+
profile is. Highly biased profiles can slow the HMMER3 acceleration
|
| 87 |
+
pipeline, by causing too many nonhomologous sequences to pass the filters.
|
| 88 |
+
|
| 89 |
+
|
| 90 |
+
.SH OPTIONS
|
| 91 |
+
|
| 92 |
+
.TP
|
| 93 |
+
.B \-h
|
| 94 |
+
Help; print a brief reminder of command line usage and all available
|
| 95 |
+
options.
|
| 96 |
+
|
| 97 |
+
|
| 98 |
+
.SH SEE ALSO
|
| 99 |
+
|
| 100 |
+
See
|
| 101 |
+
.BR hmmer (1)
|
| 102 |
+
for a master man page with a list of all the individual man pages
|
| 103 |
+
for programs in the HMMER package.
|
| 104 |
+
|
| 105 |
+
.PP
|
| 106 |
+
For complete documentation, see the user guide that came with your
|
| 107 |
+
HMMER distribution (Userguide.pdf); or see the HMMER web page
|
| 108 |
+
(http://hmmer.org/).
|
| 109 |
+
|
| 110 |
+
|
| 111 |
+
|
| 112 |
+
.SH COPYRIGHT
|
| 113 |
+
|
| 114 |
+
.nf
|
| 115 |
+
Copyright (C) 2023 Howard Hughes Medical Institute.
|
| 116 |
+
Freely distributed under the BSD open source license.
|
| 117 |
+
.fi
|
| 118 |
+
|
| 119 |
+
For additional information on copyright and licensing, see the file
|
| 120 |
+
called COPYRIGHT in your HMMER source distribution, or see the HMMER
|
| 121 |
+
web page
|
| 122 |
+
(http://hmmer.org/).
|
| 123 |
+
|
| 124 |
+
|
| 125 |
+
.SH AUTHOR
|
| 126 |
+
|
| 127 |
+
.nf
|
| 128 |
+
http://eddylab.org
|
| 129 |
+
.fi
|
| 130 |
+
|
| 131 |
+
|
| 132 |
+
|
| 133 |
+
|
flax_model/alphafold3/_tools/hmmer/share/man/man1/makehmmerdb.1
ADDED
|
@@ -0,0 +1,133 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
.TH "makehmmerdb" 1 "Aug 2023" "HMMER 3.4" "HMMER Manual"
|
| 2 |
+
|
| 3 |
+
.SH NAME
|
| 4 |
+
makehmmerdb \- build nhmmer database from a sequence file
|
| 5 |
+
|
| 6 |
+
|
| 7 |
+
.SH SYNOPSIS
|
| 8 |
+
.B makehmmerdb
|
| 9 |
+
[\fIoptions\fR]
|
| 10 |
+
.I seqfile
|
| 11 |
+
.I binaryfile
|
| 12 |
+
|
| 13 |
+
|
| 14 |
+
.SH DESCRIPTION
|
| 15 |
+
|
| 16 |
+
.PP
|
| 17 |
+
.B makehmmerdb
|
| 18 |
+
is used to create a binary file from a DNA sequence file. This
|
| 19 |
+
binary file may be used as a target database for the DNA search tool
|
| 20 |
+
.BR nhmmer .
|
| 21 |
+
Using default settings in
|
| 22 |
+
.BR nhmmer ,
|
| 23 |
+
this yields a roughly 10-fold acceleration with small loss of
|
| 24 |
+
sensitivity on benchmarks.
|
| 25 |
+
|
| 26 |
+
|
| 27 |
+
.SH OPTIONS
|
| 28 |
+
|
| 29 |
+
.TP
|
| 30 |
+
.B \-h
|
| 31 |
+
Help; print a brief reminder of command line usage and all available
|
| 32 |
+
options.
|
| 33 |
+
|
| 34 |
+
|
| 35 |
+
|
| 36 |
+
.SH OTHER OPTIONS
|
| 37 |
+
|
| 38 |
+
.TP
|
| 39 |
+
.BI \-\-informat " <s>"
|
| 40 |
+
Assert that input
|
| 41 |
+
.I seqfile
|
| 42 |
+
is in format
|
| 43 |
+
.IR <s> ,
|
| 44 |
+
bypassing format autodetection.
|
| 45 |
+
Common choices for
|
| 46 |
+
.I <s>
|
| 47 |
+
include:
|
| 48 |
+
.BR fasta ,
|
| 49 |
+
.BR embl ,
|
| 50 |
+
.BR genbank.
|
| 51 |
+
Alignment formats also work;
|
| 52 |
+
common choices include:
|
| 53 |
+
.BR stockholm ,
|
| 54 |
+
.BR a2m ,
|
| 55 |
+
.BR afa ,
|
| 56 |
+
.BR psiblast ,
|
| 57 |
+
.BR clustal ,
|
| 58 |
+
.BR phylip .
|
| 59 |
+
For more information, and for codes for some less common formats,
|
| 60 |
+
see main documentation.
|
| 61 |
+
The string
|
| 62 |
+
.I <s>
|
| 63 |
+
is case-insensitive (\fBfasta\fR or \fBFASTA\fR both work).
|
| 64 |
+
|
| 65 |
+
|
| 66 |
+
.TP
|
| 67 |
+
.BI \-\-bin_length " <n>"
|
| 68 |
+
Bin length. The binary file depends on a data structure called the
|
| 69 |
+
FM index, which organizes a permuted copy of the sequence in bins
|
| 70 |
+
of length
|
| 71 |
+
.IR <n> .
|
| 72 |
+
Longer bin length will lead to smaller files (because data is
|
| 73 |
+
captured about each bin) and possibly slower query time. The
|
| 74 |
+
default is 256. Much more than 512 may lead to notable reduction
|
| 75 |
+
in speed.
|
| 76 |
+
|
| 77 |
+
|
| 78 |
+
.TP
|
| 79 |
+
.BI \-\-sa_freq " <n>"
|
| 80 |
+
Suffix array sample rate. The FM index structure also samples from
|
| 81 |
+
the underlying suffix array for the sequence database. More frequent
|
| 82 |
+
sampling (smaller value for
|
| 83 |
+
.IR <n> )
|
| 84 |
+
will yield larger file size and faster search (until file size becomes
|
| 85 |
+
large enough to cause I/O to be a bottleneck). The default value
|
| 86 |
+
is 8. Must be a power of 2.
|
| 87 |
+
|
| 88 |
+
|
| 89 |
+
.TP
|
| 90 |
+
.BI \-\-block_size " <n>"
|
| 91 |
+
The input sequence is broken into blocks of size
|
| 92 |
+
.I <n>
|
| 93 |
+
million letters. An FM index is built for each block, rather than
|
| 94 |
+
building an FM index for the entire sequence database. Default is
|
| 95 |
+
50. Larger blocks do not seem to yield substantial speed increase.
|
| 96 |
+
|
| 97 |
+
|
| 98 |
+
|
| 99 |
+
.SH SEE ALSO
|
| 100 |
+
|
| 101 |
+
See
|
| 102 |
+
.BR hmmer (1)
|
| 103 |
+
for a master man page with a list of all the individual man pages
|
| 104 |
+
for programs in the HMMER package.
|
| 105 |
+
|
| 106 |
+
.PP
|
| 107 |
+
For complete documentation, see the user guide that came with your
|
| 108 |
+
HMMER distribution (Userguide.pdf); or see the HMMER web page
|
| 109 |
+
(http://hmmer.org/).
|
| 110 |
+
|
| 111 |
+
|
| 112 |
+
|
| 113 |
+
.SH COPYRIGHT
|
| 114 |
+
|
| 115 |
+
.nf
|
| 116 |
+
Copyright (C) 2023 Howard Hughes Medical Institute.
|
| 117 |
+
Freely distributed under the BSD open source license.
|
| 118 |
+
.fi
|
| 119 |
+
|
| 120 |
+
For additional information on copyright and licensing, see the file
|
| 121 |
+
called COPYRIGHT in your HMMER source distribution, or see the HMMER
|
| 122 |
+
web page
|
| 123 |
+
(http://hmmer.org/).
|
| 124 |
+
|
| 125 |
+
|
| 126 |
+
.SH AUTHOR
|
| 127 |
+
|
| 128 |
+
.nf
|
| 129 |
+
http://eddylab.org
|
| 130 |
+
.fi
|
| 131 |
+
|
| 132 |
+
|
| 133 |
+
|
flax_model/alphafold3/_tools/hmmer/share/man/man1/nhmmer.1
ADDED
|
@@ -0,0 +1,687 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
.TH "nhmmer" 1 "Aug 2023" "HMMER 3.4" "HMMER Manual"
|
| 2 |
+
|
| 3 |
+
.SH NAME
|
| 4 |
+
nhmmer \- search DNA queries against a DNA sequence database
|
| 5 |
+
|
| 6 |
+
|
| 7 |
+
.SH SYNOPSIS
|
| 8 |
+
.B nhmmer
|
| 9 |
+
[\fIoptions\fR]
|
| 10 |
+
.I queryfile
|
| 11 |
+
.I seqdb
|
| 12 |
+
|
| 13 |
+
|
| 14 |
+
.SH DESCRIPTION
|
| 15 |
+
|
| 16 |
+
.PP
|
| 17 |
+
.B nhmmer
|
| 18 |
+
is used to search one or more nucleotide queries against a
|
| 19 |
+
nucleotide sequence database.
|
| 20 |
+
For each query in
|
| 21 |
+
.IR queryfile ,
|
| 22 |
+
use that query to search the target database of sequences in
|
| 23 |
+
.IR seqdb ,
|
| 24 |
+
and output a ranked list of the hits with the most significant
|
| 25 |
+
matches to the query. A query may be either a profile model
|
| 26 |
+
built using
|
| 27 |
+
.BR hmmbuild ,
|
| 28 |
+
a sequence alignment, or a single sequence. Sequence based
|
| 29 |
+
queries can be in a number of formats (see
|
| 30 |
+
.BR \-\-qformat ),
|
| 31 |
+
and can typically be autodetected. Note that only
|
| 32 |
+
Stockholm
|
| 33 |
+
format supports queries made up of more than one sequence
|
| 34 |
+
alignment.
|
| 35 |
+
|
| 36 |
+
|
| 37 |
+
|
| 38 |
+
.PP
|
| 39 |
+
Either the query
|
| 40 |
+
.I queryfile
|
| 41 |
+
or the target
|
| 42 |
+
.I seqdb
|
| 43 |
+
may be '\-' (a dash character), in which case
|
| 44 |
+
the query file or target database input will be read from a <stdin> pipe instead of from a
|
| 45 |
+
file. Only one input source can come through <stdin>, not both.
|
| 46 |
+
If the
|
| 47 |
+
.I queryfile
|
| 48 |
+
contains more than one query, then
|
| 49 |
+
.I seqdb
|
| 50 |
+
cannot come from stdin, because we can't rewind the
|
| 51 |
+
streaming target database to search it with another profile.
|
| 52 |
+
|
| 53 |
+
.PP
|
| 54 |
+
If the query is sequence-based (unaligned or aligned),
|
| 55 |
+
a new file containing the HMM(s) built from the input(s) in
|
| 56 |
+
.I queryfile
|
| 57 |
+
may optionally be produced, with the filename set using the
|
| 58 |
+
.B \-\-hmmout
|
| 59 |
+
flag.
|
| 60 |
+
|
| 61 |
+
|
| 62 |
+
.PP
|
| 63 |
+
The output format is designed to be human-readable, but is often so
|
| 64 |
+
voluminous that reading it is impractical, and parsing it is a pain. The
|
| 65 |
+
.B \-\-tblout
|
| 66 |
+
option saves output in a simple tabular format that is concise and
|
| 67 |
+
easier to parse.
|
| 68 |
+
The
|
| 69 |
+
.B \-o
|
| 70 |
+
option allows redirecting the main output, including throwing it away
|
| 71 |
+
in /dev/null.
|
| 72 |
+
|
| 73 |
+
|
| 74 |
+
|
| 75 |
+
.SH OPTIONS
|
| 76 |
+
|
| 77 |
+
.TP
|
| 78 |
+
.B \-h
|
| 79 |
+
Help; print a brief reminder of command line usage and all available
|
| 80 |
+
options.
|
| 81 |
+
|
| 82 |
+
|
| 83 |
+
|
| 84 |
+
.SH OPTIONS FOR CONTROLLING OUTPUT
|
| 85 |
+
|
| 86 |
+
.TP
|
| 87 |
+
.BI \-o " <f>"
|
| 88 |
+
Direct the main human-readable output to a file
|
| 89 |
+
.I <f>
|
| 90 |
+
instead of the default stdout.
|
| 91 |
+
|
| 92 |
+
.TP
|
| 93 |
+
.BI \-A " <f>"
|
| 94 |
+
Save a multiple alignment of all significant hits (those satisfying
|
| 95 |
+
"inclusion thresholds") to the file
|
| 96 |
+
.IR <f> .
|
| 97 |
+
|
| 98 |
+
.TP
|
| 99 |
+
.BI \-\-tblout " <f>"
|
| 100 |
+
Save a simple tabular (space-delimited) file summarizing the
|
| 101 |
+
per-target output, with one data line per homologous target sequence
|
| 102 |
+
found.
|
| 103 |
+
|
| 104 |
+
.TP
|
| 105 |
+
.BI \-\-dfamtblout " <f>"
|
| 106 |
+
Save a tabular (space-delimited) file summarizing the
|
| 107 |
+
per-hit output, similar to
|
| 108 |
+
.B \-\-tblout
|
| 109 |
+
but more succinct.
|
| 110 |
+
|
| 111 |
+
.TP
|
| 112 |
+
.BI \-\-aliscoresout " <f>"
|
| 113 |
+
Save to file a list of per-position scores for each hit.
|
| 114 |
+
This is useful, for example, in identifying regions of high
|
| 115 |
+
score density for use in resolving overlapping hits from
|
| 116 |
+
different models.
|
| 117 |
+
|
| 118 |
+
.TP
|
| 119 |
+
.BI \-\-hmmout " <f>"
|
| 120 |
+
If
|
| 121 |
+
.I queryfile
|
| 122 |
+
is sequence-based, write the internally-computed HMM(s) to file
|
| 123 |
+
.IR <f> .
|
| 124 |
+
|
| 125 |
+
|
| 126 |
+
.TP
|
| 127 |
+
.B \-\-acc
|
| 128 |
+
Use accessions instead of names in the main output, where available
|
| 129 |
+
for profiles and/or sequences.
|
| 130 |
+
|
| 131 |
+
.TP
|
| 132 |
+
.B \-\-noali
|
| 133 |
+
Omit the alignment section from the main output. This can greatly
|
| 134 |
+
reduce the output volume.
|
| 135 |
+
|
| 136 |
+
.TP
|
| 137 |
+
.B \-\-notextw
|
| 138 |
+
Unlimit the length of each line in the main output. The default
|
| 139 |
+
is a limit of 120 characters per line, which helps in displaying
|
| 140 |
+
the output cleanly on terminals and in editors, but can truncate
|
| 141 |
+
target profile description lines.
|
| 142 |
+
|
| 143 |
+
.TP
|
| 144 |
+
.BI \-\-textw " <n>"
|
| 145 |
+
Set the main output's line length limit to
|
| 146 |
+
.I <n>
|
| 147 |
+
characters per line. The default is 120.
|
| 148 |
+
|
| 149 |
+
|
| 150 |
+
|
| 151 |
+
.SH OPTIONS CONTROLLING SINGLE SEQUENCE SCORING
|
| 152 |
+
|
| 153 |
+
By default, if a query is a single sequence from a file in
|
| 154 |
+
fasta format,
|
| 155 |
+
.B nhmmer
|
| 156 |
+
uses a search model constructed from that sequence and a standard
|
| 157 |
+
20x20 substitution matrix for residue probabilities, along with two
|
| 158 |
+
additional parameters for position-independent gap open and gap extend
|
| 159 |
+
probabilities. These options allow the default single-sequence scoring
|
| 160 |
+
parameters to be changed, and for single-sequence scoring options to
|
| 161 |
+
be applied to a single sequence coming from an aligned format.
|
| 162 |
+
|
| 163 |
+
.TP
|
| 164 |
+
.BI \-\-singlemx
|
| 165 |
+
If a single sequence query comes from a multiple sequence alignment file,
|
| 166 |
+
such as in Stockholm format, the search model is by default constructed as is typically done
|
| 167 |
+
for multiple sequence alignments. This option forces
|
| 168 |
+
.B nhmmer
|
| 169 |
+
to use the single-sequence method with substitution score matrix.
|
| 170 |
+
|
| 171 |
+
.TP
|
| 172 |
+
.BI \-\-mxfile "<mxfile"
|
| 173 |
+
Obtain residue alignment probabilities from the substitution matrix
|
| 174 |
+
in file
|
| 175 |
+
.IR mxfile .
|
| 176 |
+
The default score matrix is DNA1 (this matrix is internal to
|
| 177 |
+
HMMER and does not have to be available as a file).
|
| 178 |
+
The format of a substitution matrix
|
| 179 |
+
.I mxfile
|
| 180 |
+
is the standard format accepted by BLAST, FASTA, and other sequence
|
| 181 |
+
analysis software.
|
| 182 |
+
See
|
| 183 |
+
.B ftp.ncbi.nlm.nih.gov/blast/matrices/
|
| 184 |
+
for example files. (The only
|
| 185 |
+
exception: we require matrices to be square, so for DNA, use files
|
| 186 |
+
like NCBI's NUC.4.4, not NUC.4.2.)
|
| 187 |
+
|
| 188 |
+
|
| 189 |
+
.TP
|
| 190 |
+
.BI \-\-popen " <x>"
|
| 191 |
+
Set the gap open probability for a single sequence query model to
|
| 192 |
+
.IR <x> .
|
| 193 |
+
The default is 0.02.
|
| 194 |
+
.I <x>
|
| 195 |
+
must be >= 0 and < 0.5.
|
| 196 |
+
|
| 197 |
+
.TP
|
| 198 |
+
.BI \-\-pextend " <x>"
|
| 199 |
+
Set the gap extend probability for a single sequence query model to
|
| 200 |
+
.IR <x> .
|
| 201 |
+
The default is 0.4.
|
| 202 |
+
.I <x>
|
| 203 |
+
must be >= 0 and < 1.0.
|
| 204 |
+
|
| 205 |
+
|
| 206 |
+
|
| 207 |
+
.SH OPTIONS CONTROLLING REPORTING THRESHOLDS
|
| 208 |
+
|
| 209 |
+
Reporting thresholds control which hits are reported in output files
|
| 210 |
+
(the main output,
|
| 211 |
+
.BR \-\-tblout ,
|
| 212 |
+
and
|
| 213 |
+
.BR \-\-dfamtblout ).
|
| 214 |
+
Hits are ranked by statistical significance (E-value).
|
| 215 |
+
|
| 216 |
+
|
| 217 |
+
.TP
|
| 218 |
+
.BI \-E " <x>"
|
| 219 |
+
Report target sequences with an E-value of <=
|
| 220 |
+
.IR <x> .
|
| 221 |
+
The default is 10.0, meaning that on average, about 10 false positives
|
| 222 |
+
will be reported per query, so you can see the top of the noise
|
| 223 |
+
and decide for yourself if it's really noise.
|
| 224 |
+
|
| 225 |
+
.TP
|
| 226 |
+
.BI \-T " <x>"
|
| 227 |
+
Instead of thresholding output on E-value, instead
|
| 228 |
+
report target sequences with a bit score of >=
|
| 229 |
+
.IR <x> .
|
| 230 |
+
|
| 231 |
+
|
| 232 |
+
|
| 233 |
+
|
| 234 |
+
.SH OPTIONS FOR INCLUSION THRESHOLDS
|
| 235 |
+
|
| 236 |
+
Inclusion thresholds are stricter than reporting thresholds.
|
| 237 |
+
Inclusion thresholds control which hits are considered to be reliable
|
| 238 |
+
enough to be included in an output alignment or a subsequent search
|
| 239 |
+
round, or marked as significant ("!") as opposed to questionable ("?")
|
| 240 |
+
in hit output.
|
| 241 |
+
|
| 242 |
+
.TP
|
| 243 |
+
.BI \-\-incE " <x>"
|
| 244 |
+
Use an E-value of <=
|
| 245 |
+
.I <x>
|
| 246 |
+
as the inclusion threshold.
|
| 247 |
+
The default is 0.01, meaning that on average, about 1 false positive
|
| 248 |
+
would be expected in every 100 searches with different query
|
| 249 |
+
sequences.
|
| 250 |
+
|
| 251 |
+
.TP
|
| 252 |
+
.BI \-\-incT " <x>"
|
| 253 |
+
Instead of using E-values for setting the inclusion threshold,
|
| 254 |
+
use a bit score of >=
|
| 255 |
+
.I <x>
|
| 256 |
+
as the inclusion threshold.
|
| 257 |
+
By default this option is unset.
|
| 258 |
+
|
| 259 |
+
|
| 260 |
+
|
| 261 |
+
.SH OPTIONS FOR MODEL-SPECIFIC SCORE THRESHOLDING
|
| 262 |
+
|
| 263 |
+
Curated profile databases may define specific bit score thresholds for
|
| 264 |
+
each profile, superseding any thresholding based on statistical
|
| 265 |
+
significance alone.
|
| 266 |
+
|
| 267 |
+
To use these options, the profile must contain the appropriate (GA,
|
| 268 |
+
TC, and/or NC) optional score threshold annotation; this is picked up
|
| 269 |
+
by
|
| 270 |
+
.B hmmbuild
|
| 271 |
+
from Stockholm format alignment files. For a nucleotide model, each
|
| 272 |
+
thresholding option has a single per-hit threshold <x>
|
| 273 |
+
This acts as if
|
| 274 |
+
.BI \-T " <x>"
|
| 275 |
+
.BI \-\-incT " <x>"
|
| 276 |
+
has been applied specifically using each model's curated thresholds.
|
| 277 |
+
|
| 278 |
+
.TP
|
| 279 |
+
.B \-\-cut_ga
|
| 280 |
+
Use the GA (gathering) bit score threshold in the model to set
|
| 281 |
+
per-hit reporting and inclusion
|
| 282 |
+
thresholds. GA thresholds are generally considered to be the
|
| 283 |
+
reliable curated thresholds defining family membership; for example,
|
| 284 |
+
in Dfam, these thresholds are applied when annotating a genome
|
| 285 |
+
with a model of a family known to be found in that organism. They
|
| 286 |
+
may allow for minimal expected false discovery rate.
|
| 287 |
+
|
| 288 |
+
.TP
|
| 289 |
+
.B \-\-cut_nc
|
| 290 |
+
Use the NC (noise cutoff) bit score threshold in the model to set
|
| 291 |
+
per-hit reporting and inclusion
|
| 292 |
+
thresholds. NC thresholds are less stringent than GA; in the context
|
| 293 |
+
of Pfam, they are generally used to store the score of the
|
| 294 |
+
highest-scoring known false positive.
|
| 295 |
+
|
| 296 |
+
.TP
|
| 297 |
+
.B \-\-cut_tc
|
| 298 |
+
Use the TC (trusted cutoff) bit score threshold in the model to set
|
| 299 |
+
per-hit reporting and inclusion
|
| 300 |
+
thresholds. TC thresholds are more stringent than GA, and are
|
| 301 |
+
generally considered to be the score of the lowest-scoring known
|
| 302 |
+
true positive that is above all known false positives; for example,
|
| 303 |
+
in Dfam, these thresholds are applied when annotating a genome
|
| 304 |
+
with a model of a family not known to be found in that organism.
|
| 305 |
+
|
| 306 |
+
|
| 307 |
+
|
| 308 |
+
|
| 309 |
+
.SH OPTIONS CONTROLLING THE ACCELERATION PIPELINE
|
| 310 |
+
|
| 311 |
+
HMMER3 searches are accelerated in a three-step filter pipeline: the
|
| 312 |
+
scanning-SSV filter, the Viterbi filter, and the Forward filter. The
|
| 313 |
+
first filter is the fastest and most approximate; the last is the full
|
| 314 |
+
Forward scoring algorithm. There is also a bias filter step between
|
| 315 |
+
SSV and Viterbi. Targets that pass all the steps in the acceleration
|
| 316 |
+
pipeline are then subjected to postprocessing -- domain
|
| 317 |
+
identification and scoring using the Forward/Backward algorithm.
|
| 318 |
+
|
| 319 |
+
Changing filter thresholds only removes or includes targets from
|
| 320 |
+
consideration; changing filter thresholds does not alter bit scores,
|
| 321 |
+
E-values, or alignments, all of which are determined solely in
|
| 322 |
+
postprocessing.
|
| 323 |
+
|
| 324 |
+
.TP
|
| 325 |
+
.B \-\-max
|
| 326 |
+
Turn off (nearly) all filters, including the bias filter, and run full
|
| 327 |
+
Forward/Backward postprocessing on most of the target sequence.
|
| 328 |
+
In contrast to
|
| 329 |
+
.B phmmer
|
| 330 |
+
and
|
| 331 |
+
.BR hmmsearch ,
|
| 332 |
+
where this flag really does turn off the filters entirely, the
|
| 333 |
+
.B \-\-max
|
| 334 |
+
flag in
|
| 335 |
+
.B nhmmer
|
| 336 |
+
sets the scanning-SSV filter threshold to 0.4, not 1.0. Use of this
|
| 337 |
+
flag increases sensitivity somewhat, at a large cost in speed.
|
| 338 |
+
|
| 339 |
+
.TP
|
| 340 |
+
.BI \-\-F1 " <x>"
|
| 341 |
+
Set the P-value threshold for the SSV filter step. The default is
|
| 342 |
+
0.02, meaning that roughly 2% of the highest scoring nonhomologous
|
| 343 |
+
targets are expected to pass the filter.
|
| 344 |
+
|
| 345 |
+
.TP
|
| 346 |
+
.BI \-\-F2 " <x>"
|
| 347 |
+
Set the P-value threshold for the Viterbi filter step.
|
| 348 |
+
The default is 0.001.
|
| 349 |
+
|
| 350 |
+
.TP
|
| 351 |
+
.BI \-\-F3 " <x>"
|
| 352 |
+
Set the P-value threshold for the Forward filter step.
|
| 353 |
+
The default is 1e-5.
|
| 354 |
+
|
| 355 |
+
.TP
|
| 356 |
+
.B \-\-nobias
|
| 357 |
+
Turn off the bias filter. This increases sensitivity somewhat, but can
|
| 358 |
+
come at a high cost in speed, especially if the query has biased
|
| 359 |
+
residue composition (such as a repetitive sequence region, or if it is
|
| 360 |
+
a membrane protein with large regions of hydrophobicity). Without the
|
| 361 |
+
bias filter, too many sequences may pass the filter with biased
|
| 362 |
+
queries, leading to slower than expected performance as the
|
| 363 |
+
computationally intensive Forward/Backward algorithms shoulder an
|
| 364 |
+
abnormally heavy load.
|
| 365 |
+
|
| 366 |
+
|
| 367 |
+
|
| 368 |
+
.SH OPTIONS FOR SPECIFYING THE ALPHABET
|
| 369 |
+
|
| 370 |
+
.TP
|
| 371 |
+
.B \-\-dna
|
| 372 |
+
Assert that sequences in
|
| 373 |
+
.I msafile
|
| 374 |
+
are DNA, bypassing alphabet autodetection.
|
| 375 |
+
|
| 376 |
+
.TP
|
| 377 |
+
.B \-\-rna
|
| 378 |
+
Assert that sequences in
|
| 379 |
+
.I msafile
|
| 380 |
+
are RNA, bypassing alphabet autodetection.
|
| 381 |
+
|
| 382 |
+
|
| 383 |
+
|
| 384 |
+
.SH OPTIONS CONTROLLING SEED SEARCH HEURISTIC
|
| 385 |
+
|
| 386 |
+
When searching with
|
| 387 |
+
.BR nhmmer ,
|
| 388 |
+
one may optionally precompute a binary version of the target database, using
|
| 389 |
+
.BR makehmmerdb ,
|
| 390 |
+
then search against that database. Using default settings, this yields a
|
| 391 |
+
roughly 10-fold acceleration with small loss of sensitivity on benchmarks.
|
| 392 |
+
This is achieved using a heuristic method that searches for seeds (ungapped
|
| 393 |
+
alignments) around which full processing is done. This is essentially
|
| 394 |
+
a replacement to the SSV stage. (This method has been extensively tested,
|
| 395 |
+
but should still be treated as somewhat experimental.)
|
| 396 |
+
The following options only impact
|
| 397 |
+
.B nhmmer
|
| 398 |
+
if the value of
|
| 399 |
+
.B \-\-tformat
|
| 400 |
+
is
|
| 401 |
+
.BR hmmerdb .
|
| 402 |
+
|
| 403 |
+
Changing parameters for this seed-finding step will impact both speed and
|
| 404 |
+
sensitivity - typically faster search leads to lower sensitivity.
|
| 405 |
+
|
| 406 |
+
.TP
|
| 407 |
+
.BI \-\-seed_max_depth " <n>"
|
| 408 |
+
The seed step requires that a seed reach a specified bit score in length
|
| 409 |
+
no longer than
|
| 410 |
+
.IR <n> .
|
| 411 |
+
By default, this value is 15. Longer seeds allow a greater chance of
|
| 412 |
+
meeting the bit score threshold, leading to diminished filtering
|
| 413 |
+
(greater sensitivity, slower run time).
|
| 414 |
+
|
| 415 |
+
.TP
|
| 416 |
+
.BI \-\-seed_sc_thresh " <x>"
|
| 417 |
+
The seed must reach score
|
| 418 |
+
.I <x>
|
| 419 |
+
(in bits). The default is 15.0 bits. A higher threshold increases
|
| 420 |
+
filtering stringency, leading to faster run times and lower
|
| 421 |
+
sensitivity.
|
| 422 |
+
|
| 423 |
+
.TP
|
| 424 |
+
.BI \-\-seed_sc_density " <x>"
|
| 425 |
+
Either all prefixes or all suffixes of a seed must have
|
| 426 |
+
bit density (bits per aligned position) of at least
|
| 427 |
+
.IR <x> .
|
| 428 |
+
The default is 0.8 bits/position. An increase in the density
|
| 429 |
+
requirement leads to increased filtering stringency, thus faster
|
| 430 |
+
run times and lower sensitivity.
|
| 431 |
+
|
| 432 |
+
.TP
|
| 433 |
+
.BI \-\-seed_drop_max_len " <n>"
|
| 434 |
+
A seed may not have a run of length
|
| 435 |
+
.I <n>
|
| 436 |
+
in which the score drops by
|
| 437 |
+
.B \-\-seed_drop_lim
|
| 438 |
+
or more. Basically, this prunes seeds that go through long
|
| 439 |
+
slightly-negative seed extensions. The default is 4. Increasing
|
| 440 |
+
the limit causes (slightly) diminished filtering efficiency, thus
|
| 441 |
+
slower run times and higher sensitivity. (minor tuning option)
|
| 442 |
+
|
| 443 |
+
.TP
|
| 444 |
+
.BI \-\-seed_drop_lim " <x>"
|
| 445 |
+
In a seed, there may be no run of length
|
| 446 |
+
.B \-\-seed_drop_max_len
|
| 447 |
+
in which the score drops by
|
| 448 |
+
.BR \-\-seed_drop_lim .
|
| 449 |
+
The default is 0.3 bits. Larger numbers mean less filtering.
|
| 450 |
+
(minor tuning option)
|
| 451 |
+
|
| 452 |
+
.TP
|
| 453 |
+
.BI \-\-seed_req_pos " <n>"
|
| 454 |
+
A seed must contain a run of at least
|
| 455 |
+
.I <n>
|
| 456 |
+
positive-scoring matches. The default is 5. Larger values mean
|
| 457 |
+
increased filtering.
|
| 458 |
+
(minor tuning option)
|
| 459 |
+
|
| 460 |
+
.TP
|
| 461 |
+
.BI \-\-seed_ssv_length " <n>"
|
| 462 |
+
After finding a short seed, an ungapped alignment is extended
|
| 463 |
+
in both directions in an attempt to meet the
|
| 464 |
+
.B \-\-F1
|
| 465 |
+
score threshold. The window through which this ungapped alignment
|
| 466 |
+
extends is length
|
| 467 |
+
.IR <n> .
|
| 468 |
+
The default is 70.
|
| 469 |
+
Decreasing this value slightly reduces run time, at a small risk of
|
| 470 |
+
reduced sensitivity. (minor tuning option)
|
| 471 |
+
|
| 472 |
+
|
| 473 |
+
.SH OTHER OPTIONS
|
| 474 |
+
|
| 475 |
+
|
| 476 |
+
.TP
|
| 477 |
+
.BI \-\-qformat " <s>"
|
| 478 |
+
Assert that input
|
| 479 |
+
.I queryfile
|
| 480 |
+
is a sequence file (unaligned or aligned), in format
|
| 481 |
+
.IR <s> ,
|
| 482 |
+
bypassing format autodetection.
|
| 483 |
+
Common choices for
|
| 484 |
+
.I <s>
|
| 485 |
+
include:
|
| 486 |
+
.BR fasta ,
|
| 487 |
+
.BR embl ,
|
| 488 |
+
.BR genbank.
|
| 489 |
+
Alignment formats also work, and will serve as the
|
| 490 |
+
basis for automatic creation of a profile HMM used for
|
| 491 |
+
searching;
|
| 492 |
+
common choices include:
|
| 493 |
+
.BR stockholm ,
|
| 494 |
+
.BR a2m ,
|
| 495 |
+
.BR afa ,
|
| 496 |
+
.BR psiblast ,
|
| 497 |
+
.BR clustal ,
|
| 498 |
+
.BR phylip .
|
| 499 |
+
For more information, and for codes for some less common formats,
|
| 500 |
+
see main documentation.
|
| 501 |
+
|
| 502 |
+
|
| 503 |
+
.TP
|
| 504 |
+
.BI \-\-qsingle_seqs
|
| 505 |
+
Force
|
| 506 |
+
.I queryfile
|
| 507 |
+
to be read as individual sequences, even if it is in
|
| 508 |
+
an msa format. For example, if the input is in aligned
|
| 509 |
+
.BR stockholm
|
| 510 |
+
format, the
|
| 511 |
+
.BR \-\-qsingle_seqs
|
| 512 |
+
flag will cause each sequence in that alignment to be used as a separate query sequence.
|
| 513 |
+
|
| 514 |
+
.TP
|
| 515 |
+
.BI \-\-tformat " <s>"
|
| 516 |
+
Assert that target sequence database
|
| 517 |
+
.I seqdb
|
| 518 |
+
is in format
|
| 519 |
+
.IR <s> ,
|
| 520 |
+
bypassing format autodetection.
|
| 521 |
+
Common choices for
|
| 522 |
+
.I <s>
|
| 523 |
+
include:
|
| 524 |
+
.BR fasta ,
|
| 525 |
+
.BR embl ,
|
| 526 |
+
.BR genbank ,
|
| 527 |
+
.BR fmindex .
|
| 528 |
+
Alignment formats also work;
|
| 529 |
+
common choices include:
|
| 530 |
+
.BR stockholm ,
|
| 531 |
+
.BR a2m ,
|
| 532 |
+
.BR afa ,
|
| 533 |
+
.BR psiblast ,
|
| 534 |
+
.BR clustal ,
|
| 535 |
+
.BR phylip .
|
| 536 |
+
For more information, and for codes for some less common formats,
|
| 537 |
+
see main documentation.
|
| 538 |
+
The string
|
| 539 |
+
.I <s>
|
| 540 |
+
is case-insensitive (\fBfasta\fR or \fBFASTA\fR both work).
|
| 541 |
+
The format
|
| 542 |
+
.B fmindex
|
| 543 |
+
indicates that the database file is a binary file produced using
|
| 544 |
+
.BR makehmmerdb .
|
| 545 |
+
|
| 546 |
+
|
| 547 |
+
.TP
|
| 548 |
+
.B \-\-nonull2
|
| 549 |
+
Turn off the null2 score corrections for biased composition.
|
| 550 |
+
|
| 551 |
+
.TP
|
| 552 |
+
.BI \-Z " <x>"
|
| 553 |
+
For the purposes of per-hit E-value calculations,
|
| 554 |
+
Assert that the total size of the target database is
|
| 555 |
+
.I <x>
|
| 556 |
+
million nucleotides,
|
| 557 |
+
rather than the actual number of targets seen.
|
| 558 |
+
|
| 559 |
+
|
| 560 |
+
.TP
|
| 561 |
+
.BI \-\-seed " <n>"
|
| 562 |
+
Set the random number seed to
|
| 563 |
+
.IR <n> .
|
| 564 |
+
Some steps in postprocessing require Monte Carlo simulation. The
|
| 565 |
+
default is to use a fixed seed (42), so that results are exactly
|
| 566 |
+
reproducible. Any other positive integer will give different (but also
|
| 567 |
+
reproducible) results. A choice of 0 uses a randomly chosen seed.
|
| 568 |
+
|
| 569 |
+
|
| 570 |
+
.TP
|
| 571 |
+
.BI \-\-w_beta " <x>"
|
| 572 |
+
Window length tail mass.
|
| 573 |
+
The upper bound,
|
| 574 |
+
.IR W ,
|
| 575 |
+
on the length at which nhmmer expects to find an instance of the
|
| 576 |
+
model is set such that the fraction of all sequences generated
|
| 577 |
+
by the model with length >= W is less than
|
| 578 |
+
.IR <x> .
|
| 579 |
+
The default is 1e-7.
|
| 580 |
+
This flag may be used to override the value of
|
| 581 |
+
.I W
|
| 582 |
+
established for the model by
|
| 583 |
+
.BR hmmbuild ,
|
| 584 |
+
or when the query is sequence-based.
|
| 585 |
+
|
| 586 |
+
|
| 587 |
+
|
| 588 |
+
.TP
|
| 589 |
+
.BI \-\-w_length " <n>"
|
| 590 |
+
Override the model instance length upper bound, W,
|
| 591 |
+
which is otherwise controlled by
|
| 592 |
+
.BR \-\-w_beta .
|
| 593 |
+
It should be larger than the model length. The value of W
|
| 594 |
+
is used deep in the acceleration pipeline, and modest changes
|
| 595 |
+
are not expected to impact results (though larger values of W
|
| 596 |
+
do lead to longer run time).
|
| 597 |
+
This flag may be used to override the value of
|
| 598 |
+
W established for the model by
|
| 599 |
+
.BR hmmbuild ,
|
| 600 |
+
or when the query is sequence-based.
|
| 601 |
+
|
| 602 |
+
|
| 603 |
+
|
| 604 |
+
.TP
|
| 605 |
+
.B \-\-watson
|
| 606 |
+
Only search the top strand. By default both the query sequence
|
| 607 |
+
and its reverse-complement are searched.
|
| 608 |
+
|
| 609 |
+
.TP
|
| 610 |
+
.B \-\-crick
|
| 611 |
+
Only search the bottom (reverse-complement) strand. By
|
| 612 |
+
default both the query sequence and its reverse-complement are searched.
|
| 613 |
+
|
| 614 |
+
|
| 615 |
+
.TP
|
| 616 |
+
.BI \-\-cpu " <n>"
|
| 617 |
+
Set the number of parallel worker threads to
|
| 618 |
+
.IR <n> .
|
| 619 |
+
On multicore machines, the default is 2.
|
| 620 |
+
You can also control this number by setting an environment variable,
|
| 621 |
+
.IR HMMER_NCPU .
|
| 622 |
+
There is also a master thread, so the actual number of threads that
|
| 623 |
+
HMMER spawns is
|
| 624 |
+
.IR <n> +1.
|
| 625 |
+
|
| 626 |
+
This option is not available if HMMER was compiled with POSIX threads
|
| 627 |
+
support turned off.
|
| 628 |
+
|
| 629 |
+
|
| 630 |
+
|
| 631 |
+
|
| 632 |
+
.TP
|
| 633 |
+
.BI \-\-stall
|
| 634 |
+
For debugging the MPI master/worker version: pause after start, to
|
| 635 |
+
enable the developer to attach debuggers to the running master and
|
| 636 |
+
worker(s) processes. Send SIGCONT signal to release the pause.
|
| 637 |
+
(Under gdb:
|
| 638 |
+
.BR "(gdb) signal SIGCONT" )
|
| 639 |
+
(Only available if optional MPI support was enabled at compile-time.)
|
| 640 |
+
|
| 641 |
+
.TP
|
| 642 |
+
.BI \-\-mpi
|
| 643 |
+
Run under MPI control with master/worker parallelization (using
|
| 644 |
+
.BR mpirun ,
|
| 645 |
+
for example, or equivalent). Only available if optional MPI support
|
| 646 |
+
was enabled at compile-time.
|
| 647 |
+
|
| 648 |
+
|
| 649 |
+
|
| 650 |
+
|
| 651 |
+
|
| 652 |
+
|
| 653 |
+
.SH SEE ALSO
|
| 654 |
+
|
| 655 |
+
See
|
| 656 |
+
.BR hmmer (1)
|
| 657 |
+
for a master man page with a list of all the individual man pages
|
| 658 |
+
for programs in the HMMER package.
|
| 659 |
+
|
| 660 |
+
.PP
|
| 661 |
+
For complete documentation, see the user guide that came with your
|
| 662 |
+
HMMER distribution (Userguide.pdf); or see the HMMER web page
|
| 663 |
+
(http://hmmer.org/).
|
| 664 |
+
|
| 665 |
+
|
| 666 |
+
|
| 667 |
+
.SH COPYRIGHT
|
| 668 |
+
|
| 669 |
+
.nf
|
| 670 |
+
Copyright (C) 2023 Howard Hughes Medical Institute.
|
| 671 |
+
Freely distributed under the BSD open source license.
|
| 672 |
+
.fi
|
| 673 |
+
|
| 674 |
+
For additional information on copyright and licensing, see the file
|
| 675 |
+
called COPYRIGHT in your HMMER source distribution, or see the HMMER
|
| 676 |
+
web page
|
| 677 |
+
(http://hmmer.org/).
|
| 678 |
+
|
| 679 |
+
|
| 680 |
+
.SH AUTHOR
|
| 681 |
+
|
| 682 |
+
.nf
|
| 683 |
+
http://eddylab.org
|
| 684 |
+
.fi
|
| 685 |
+
|
| 686 |
+
|
| 687 |
+
|
flax_model/alphafold3/_tools/hmmer/share/man/man1/nhmmscan.1
ADDED
|
@@ -0,0 +1,472 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
.TH "nhmmscan" 1 "Aug 2023" "HMMER 3.4" "HMMER Manual"
|
| 2 |
+
|
| 3 |
+
.SH NAME
|
| 4 |
+
nhmmscan \- search DNA sequence(s) against a DNA profile database
|
| 5 |
+
|
| 6 |
+
|
| 7 |
+
.SH SYNOPSIS
|
| 8 |
+
.B nhmmscan
|
| 9 |
+
[\fIoptions\fR]
|
| 10 |
+
.I hmmdb
|
| 11 |
+
.I seqfile
|
| 12 |
+
|
| 13 |
+
|
| 14 |
+
|
| 15 |
+
.SH DESCRIPTION
|
| 16 |
+
|
| 17 |
+
.PP
|
| 18 |
+
.B nhmmscan
|
| 19 |
+
is used to search nucleotide sequences against collections
|
| 20 |
+
of nucleotide profiles. For each sequence in
|
| 21 |
+
.IR seqfile ,
|
| 22 |
+
use that query sequence to search the target database of
|
| 23 |
+
profiles in
|
| 24 |
+
.IR hmmdb ,
|
| 25 |
+
and output ranked lists of the profiles with the
|
| 26 |
+
most significant matches to the sequence.
|
| 27 |
+
|
| 28 |
+
.PP
|
| 29 |
+
The
|
| 30 |
+
.I seqfile
|
| 31 |
+
may contain more than one query sequence. It can be in FASTA format,
|
| 32 |
+
or several other common sequence file formats (genbank, embl, and
|
| 33 |
+
uniprot, among others), or in alignment file formats (stockholm,
|
| 34 |
+
aligned fasta, and others). See the
|
| 35 |
+
.I \-\-qformat
|
| 36 |
+
option for a complete list.
|
| 37 |
+
|
| 38 |
+
.PP
|
| 39 |
+
The
|
| 40 |
+
.I hmmdb
|
| 41 |
+
needs to be press'ed using
|
| 42 |
+
.B hmmpress
|
| 43 |
+
before it can be searched with
|
| 44 |
+
.BR nhmmscan .
|
| 45 |
+
This creates four binary files,
|
| 46 |
+
suffixed
|
| 47 |
+
.B .h3{fimp}.
|
| 48 |
+
|
| 49 |
+
.PP
|
| 50 |
+
The query
|
| 51 |
+
.I seqfile
|
| 52 |
+
may be '\-' (a dash character), in which case
|
| 53 |
+
the query sequences are read from a stdin pipe instead of from a
|
| 54 |
+
file.
|
| 55 |
+
The
|
| 56 |
+
.I hmmdb
|
| 57 |
+
cannot be read from a stdin stream, because it needs to have
|
| 58 |
+
the four auxiliary binary files generated by
|
| 59 |
+
.BR hmmpress .
|
| 60 |
+
|
| 61 |
+
.PP
|
| 62 |
+
The output format is designed to be human-readable, but is often so
|
| 63 |
+
voluminous that reading it is impractical, and parsing it is a pain. The
|
| 64 |
+
.B \-\-tblout
|
| 65 |
+
option saves output in a simple tabular format that is concise and
|
| 66 |
+
easier to parse.
|
| 67 |
+
The
|
| 68 |
+
.B \-o
|
| 69 |
+
option allows redirecting the main output, including throwing it away
|
| 70 |
+
in /dev/null.
|
| 71 |
+
|
| 72 |
+
|
| 73 |
+
|
| 74 |
+
.SH OPTIONS
|
| 75 |
+
|
| 76 |
+
.TP
|
| 77 |
+
.B \-h
|
| 78 |
+
Help; print a brief reminder of command line usage and all available
|
| 79 |
+
options.
|
| 80 |
+
|
| 81 |
+
|
| 82 |
+
|
| 83 |
+
.SH OPTIONS FOR CONTROLLING OUTPUT
|
| 84 |
+
|
| 85 |
+
.TP
|
| 86 |
+
.BI \-o " <f>"
|
| 87 |
+
Direct the main human-readable output to a file
|
| 88 |
+
.I <f>
|
| 89 |
+
instead of the default stdout.
|
| 90 |
+
|
| 91 |
+
.TP
|
| 92 |
+
.BI \-\-tblout " <f>"
|
| 93 |
+
Save a simple tabular (space-delimited) file summarizing the
|
| 94 |
+
per-hit output, with one data line per homologous target model
|
| 95 |
+
hit found.
|
| 96 |
+
|
| 97 |
+
.TP
|
| 98 |
+
.BI \-\-dfamtblout " <f>"
|
| 99 |
+
Save a tabular (space-delimited) file summarizing the
|
| 100 |
+
per-hit output, similar to
|
| 101 |
+
.B \-\-tblout
|
| 102 |
+
but more succinct.
|
| 103 |
+
|
| 104 |
+
.TP
|
| 105 |
+
.BI \-\-aliscoresout " <f>"
|
| 106 |
+
Save to file a list of per-position scores for each hit.
|
| 107 |
+
This is useful, for example, in identifying regions of high
|
| 108 |
+
score density for use in resolving overlapping hits from
|
| 109 |
+
different models.
|
| 110 |
+
|
| 111 |
+
|
| 112 |
+
.TP
|
| 113 |
+
.B \-\-acc
|
| 114 |
+
Use accessions instead of names in the main output, where available
|
| 115 |
+
for profiles and/or sequences.
|
| 116 |
+
|
| 117 |
+
.TP
|
| 118 |
+
.B \-\-noali
|
| 119 |
+
Omit the alignment section from the main output. This can greatly
|
| 120 |
+
reduce the output volume.
|
| 121 |
+
|
| 122 |
+
.TP
|
| 123 |
+
.B \-\-notextw
|
| 124 |
+
Unlimit the length of each line in the main output. The default
|
| 125 |
+
is a limit of 120 characters per line, which helps in displaying
|
| 126 |
+
the output cleanly on terminals and in editors, but can truncate
|
| 127 |
+
target profile description lines.
|
| 128 |
+
|
| 129 |
+
.TP
|
| 130 |
+
.BI \-\-textw " <n>"
|
| 131 |
+
Set the main output's line length limit to
|
| 132 |
+
.I <n>
|
| 133 |
+
characters per line. The default is 120.
|
| 134 |
+
|
| 135 |
+
|
| 136 |
+
|
| 137 |
+
.SH OPTIONS FOR REPORTING THRESHOLDS
|
| 138 |
+
|
| 139 |
+
Reporting thresholds control which hits are reported in output files
|
| 140 |
+
(the main output,
|
| 141 |
+
.BR \-\-tblout ,
|
| 142 |
+
and
|
| 143 |
+
.BR \-\-dfamtblout ).
|
| 144 |
+
Hits are ranked by statistical significance (E-value).
|
| 145 |
+
|
| 146 |
+
.TP
|
| 147 |
+
.BI \-E " <x>"
|
| 148 |
+
Report target profiles with an E-value of <=
|
| 149 |
+
.IR <x> .
|
| 150 |
+
The default is 10.0, meaning that on average, about 10 false positives
|
| 151 |
+
will be reported per query, so you can see the top of the noise
|
| 152 |
+
and decide for yourself if it's really noise.
|
| 153 |
+
|
| 154 |
+
.TP
|
| 155 |
+
.BI \-T " <x>"
|
| 156 |
+
Instead of thresholding output on E-value, instead
|
| 157 |
+
report target profiles with a bit score of >=
|
| 158 |
+
.IR <x> .
|
| 159 |
+
|
| 160 |
+
|
| 161 |
+
|
| 162 |
+
|
| 163 |
+
.SH OPTIONS FOR INCLUSION THRESHOLDS
|
| 164 |
+
|
| 165 |
+
Inclusion thresholds are stricter than reporting thresholds.
|
| 166 |
+
Inclusion thresholds control which hits are considered to be
|
| 167 |
+
reliable enough
|
| 168 |
+
to be included in an output alignment or a subsequent search round.
|
| 169 |
+
In
|
| 170 |
+
.BR nhmmscan ,
|
| 171 |
+
which does not have any alignment output (like
|
| 172 |
+
.BR nhmmer ),
|
| 173 |
+
inclusion thresholds have little effect. They only affect what hits
|
| 174 |
+
get marked as significant (!) or questionable (?) in hit
|
| 175 |
+
output.
|
| 176 |
+
|
| 177 |
+
.TP
|
| 178 |
+
.BI \-\-incE " <x>"
|
| 179 |
+
Use an E-value of <=
|
| 180 |
+
.I <x>
|
| 181 |
+
as the inclusion threshold.
|
| 182 |
+
The default is 0.01, meaning that on average, about 1 false positive
|
| 183 |
+
would be expected in every 100 searches with different query
|
| 184 |
+
sequences.
|
| 185 |
+
|
| 186 |
+
.TP
|
| 187 |
+
.BI \-\-incT " <x>"
|
| 188 |
+
Instead of using E-values for setting the inclusion threshold,
|
| 189 |
+
use a bit score of >=
|
| 190 |
+
.I <x>
|
| 191 |
+
as the inclusion threshold.
|
| 192 |
+
It would be unusual to use bit score thresholds with
|
| 193 |
+
.BR hmmscan ,
|
| 194 |
+
because you don't expect a single score threshold to work for
|
| 195 |
+
different profiles; different profiles have slightly different
|
| 196 |
+
expected score distributions.
|
| 197 |
+
|
| 198 |
+
|
| 199 |
+
|
| 200 |
+
.SH OPTIONS FOR MODEL-SPECIFIC SCORE THRESHOLDING
|
| 201 |
+
|
| 202 |
+
Curated profile databases may define specific bit score thresholds for
|
| 203 |
+
each profile, superseding any thresholding based on statistical
|
| 204 |
+
significance alone.
|
| 205 |
+
|
| 206 |
+
To use these options, the profile must contain the appropriate (GA,
|
| 207 |
+
TC, and/or NC) optional score threshold annotation; this is picked up
|
| 208 |
+
by
|
| 209 |
+
.B hmmbuild
|
| 210 |
+
from Stockholm format alignment files. For a nucleotide model, each
|
| 211 |
+
thresholding option has a single per-hit threshold <x>
|
| 212 |
+
This acts as if
|
| 213 |
+
.BI \-T " <x>"
|
| 214 |
+
.BI \-\-incT " <x>"
|
| 215 |
+
has been applied specifically using each model's curated thresholds.
|
| 216 |
+
|
| 217 |
+
.TP
|
| 218 |
+
.B \-\-cut_ga
|
| 219 |
+
Use the GA (gathering) bit score threshold in the model to set
|
| 220 |
+
per-hit reporting and inclusion
|
| 221 |
+
thresholds. GA thresholds are generally considered to be the
|
| 222 |
+
reliable curated thresholds defining family membership; for example,
|
| 223 |
+
in Dfam, these thresholds are applied when annotating a genome
|
| 224 |
+
with a model of a family known to be found in that organism. They
|
| 225 |
+
may allow for minimal expected false discovery rate.
|
| 226 |
+
|
| 227 |
+
.TP
|
| 228 |
+
.B \-\-cut_nc
|
| 229 |
+
Use the NC (noise cutoff) bit score threshold in the model to set
|
| 230 |
+
per-hit reporting and inclusion
|
| 231 |
+
thresholds. NC thresholds are less stringent than GA; in the context
|
| 232 |
+
of Pfam, they are generally used to store the score of the
|
| 233 |
+
highest-scoring known false positive.
|
| 234 |
+
|
| 235 |
+
.TP
|
| 236 |
+
.B \-\-cut_tc
|
| 237 |
+
Use the TC (trusted cutoff) bit score threshold in the model to set
|
| 238 |
+
per-hit reporting and inclusion
|
| 239 |
+
thresholds. TC thresholds are more stringent than GA, and are
|
| 240 |
+
generally considered to be the score of the lowest-scoring known
|
| 241 |
+
true positive that is above all known false positives; for example,
|
| 242 |
+
in Dfam, these thresholds are applied when annotating a genome
|
| 243 |
+
with a model of a family not known to be found in that organism.
|
| 244 |
+
|
| 245 |
+
|
| 246 |
+
|
| 247 |
+
.SH CONTROL OF THE ACCELERATION PIPELINE
|
| 248 |
+
|
| 249 |
+
HMMER3 searches are accelerated in a three-step filter pipeline: the
|
| 250 |
+
scanning-SSV filter, the Viterbi filter, and the Forward filter. The
|
| 251 |
+
first filter is the fastest and most approximate; the last is the full
|
| 252 |
+
Forward scoring algorithm. There is also a bias filter step between
|
| 253 |
+
SSV and Viterbi. Targets that pass all the steps in the acceleration
|
| 254 |
+
pipeline are then subjected to postprocessing -- domain
|
| 255 |
+
identification and scoring using the Forward/Backward algorithm.
|
| 256 |
+
|
| 257 |
+
Changing filter thresholds only removes or includes targets from
|
| 258 |
+
consideration; changing filter thresholds does not alter bit scores,
|
| 259 |
+
E-values, or alignments, all of which are determined solely in
|
| 260 |
+
postprocessing.
|
| 261 |
+
|
| 262 |
+
.TP
|
| 263 |
+
.B \-\-max
|
| 264 |
+
Turn off (nearly) all filters, including the bias filter, and run full
|
| 265 |
+
Forward/Backward postprocessing on most of the target sequence.
|
| 266 |
+
In contrast to
|
| 267 |
+
.B hmmscan,
|
| 268 |
+
where this flag really does turn off the filters entirely, the
|
| 269 |
+
.B \-\-max
|
| 270 |
+
flag in
|
| 271 |
+
.B nhmmscan
|
| 272 |
+
sets the scanning-SSV filter threshold to 0.4, not 1.0. Use of this
|
| 273 |
+
flag increases sensitivity somewhat, at a large cost in speed.
|
| 274 |
+
|
| 275 |
+
.TP
|
| 276 |
+
.BI \-\-F1 " <x>"
|
| 277 |
+
Set the P-value threshold for the MSV filter step. The default is
|
| 278 |
+
0.02, meaning that roughly 2% of the highest scoring nonhomologous
|
| 279 |
+
targets are expected to pass the filter.
|
| 280 |
+
|
| 281 |
+
.TP
|
| 282 |
+
.BI \-\-F2 " <x>"
|
| 283 |
+
Set the P-value threshold for the Viterbi filter step.
|
| 284 |
+
The default is 0.001.
|
| 285 |
+
|
| 286 |
+
.TP
|
| 287 |
+
.BI \-\-F3 " <x>"
|
| 288 |
+
Set the P-value threshold for the Forward filter step.
|
| 289 |
+
The default is 1e-5.
|
| 290 |
+
|
| 291 |
+
.TP
|
| 292 |
+
.B \-\-nobias
|
| 293 |
+
Turn off the bias filter. This increases sensitivity somewhat, but can
|
| 294 |
+
come at a high cost in speed, especially if the query has biased
|
| 295 |
+
residue composition (such as a repetitive sequence region, or if it is
|
| 296 |
+
a membrane protein with large regions of hydrophobicity). Without the
|
| 297 |
+
bias filter, too many sequences may pass the filter with biased
|
| 298 |
+
queries, leading to slower than expected performance as the
|
| 299 |
+
computationally intensive Forward/Backward algorithms shoulder an
|
| 300 |
+
abnormally heavy load.
|
| 301 |
+
|
| 302 |
+
|
| 303 |
+
|
| 304 |
+
.SH OTHER OPTIONS
|
| 305 |
+
|
| 306 |
+
.TP
|
| 307 |
+
.B \-\-nonull2
|
| 308 |
+
Turn off the null2 score corrections for biased composition.
|
| 309 |
+
|
| 310 |
+
.TP
|
| 311 |
+
.BI \-Z " <x>"
|
| 312 |
+
Assert that the total number of targets in your searches is
|
| 313 |
+
.IR <x> ,
|
| 314 |
+
for the purposes of per-sequence E-value calculations,
|
| 315 |
+
rather than the actual number of targets seen.
|
| 316 |
+
|
| 317 |
+
.TP
|
| 318 |
+
.BI \-\-seed " <n>"
|
| 319 |
+
Set the random number seed to
|
| 320 |
+
.IR <n> .
|
| 321 |
+
Some steps in postprocessing require Monte Carlo simulation. The
|
| 322 |
+
default is to use a fixed seed (42), so that results are exactly
|
| 323 |
+
reproducible. Any other positive integer will give different (but also
|
| 324 |
+
reproducible) results. A choice of 0 uses an arbitrarily chosen seed.
|
| 325 |
+
|
| 326 |
+
.TP
|
| 327 |
+
.BI \-\-qformat " <s>"
|
| 328 |
+
Assert that input query
|
| 329 |
+
.I seqfile
|
| 330 |
+
is in format
|
| 331 |
+
.IR <s> ,
|
| 332 |
+
bypassing format autodetection.
|
| 333 |
+
Common choices for
|
| 334 |
+
.I <s>
|
| 335 |
+
include:
|
| 336 |
+
.BR fasta ,
|
| 337 |
+
.BR embl ,
|
| 338 |
+
.BR genbank.
|
| 339 |
+
Alignment formats also work;
|
| 340 |
+
common choices include:
|
| 341 |
+
.BR stockholm ,
|
| 342 |
+
.BR a2m ,
|
| 343 |
+
.BR afa ,
|
| 344 |
+
.BR psiblast ,
|
| 345 |
+
.BR clustal ,
|
| 346 |
+
.BR phylip .
|
| 347 |
+
For more information, and for codes for some less common formats,
|
| 348 |
+
see main documentation.
|
| 349 |
+
The string
|
| 350 |
+
.I <s>
|
| 351 |
+
is case-insensitive (\fBfasta\fR or \fBFASTA\fR both work).
|
| 352 |
+
|
| 353 |
+
|
| 354 |
+
.TP
|
| 355 |
+
.BI \-\-w_beta " <x>"
|
| 356 |
+
Window length tail mass.
|
| 357 |
+
The upper bound, W,
|
| 358 |
+
on the length at which nhmmer expects to find an instance of the
|
| 359 |
+
model is set such that the fraction of all sequences generated
|
| 360 |
+
by the model with length >= W is less than
|
| 361 |
+
.IR <x> .
|
| 362 |
+
The default is 1e-7.
|
| 363 |
+
This flag may be used to override the value of W
|
| 364 |
+
established for the model by
|
| 365 |
+
.BR hmmbuild .
|
| 366 |
+
|
| 367 |
+
|
| 368 |
+
|
| 369 |
+
.TP
|
| 370 |
+
.BI \-\-w_length " <n>"
|
| 371 |
+
Override the model instance length upper bound, W,
|
| 372 |
+
which is otherwise controlled by
|
| 373 |
+
.BR \-\-w_beta .
|
| 374 |
+
It should be larger than the model length. The value of W
|
| 375 |
+
is used deep in the acceleration pipeline, and modest changes
|
| 376 |
+
are not expected to impact results (though larger values of W
|
| 377 |
+
do lead to longer run time).
|
| 378 |
+
This flag may be used to override the value of W
|
| 379 |
+
established for the model by
|
| 380 |
+
.BR hmmbuild .
|
| 381 |
+
|
| 382 |
+
|
| 383 |
+
.TP
|
| 384 |
+
.B \-\-watson
|
| 385 |
+
Only search the top strand. By default both the query sequence
|
| 386 |
+
and its reverse-complement are searched.
|
| 387 |
+
|
| 388 |
+
.TP
|
| 389 |
+
.B \-\-crick
|
| 390 |
+
Only search the bottom (reverse-complement) strand. By
|
| 391 |
+
default both the query sequence and its reverse-complement are searched.
|
| 392 |
+
|
| 393 |
+
|
| 394 |
+
.TP
|
| 395 |
+
.BI \-\-cpu " <n>"
|
| 396 |
+
Set the number of parallel worker threads to
|
| 397 |
+
.IR <n> .
|
| 398 |
+
The default is 0, meaning off (no thread-level parallelization), because
|
| 399 |
+
.B nhmmscan
|
| 400 |
+
is typically i/o bound and the extra overhead of our current
|
| 401 |
+
multithreaded
|
| 402 |
+
implementation isn't worthwhile.
|
| 403 |
+
You can also control this number by setting an environment variable,
|
| 404 |
+
.IR HMMER_NCPU .
|
| 405 |
+
There is also a master thread, so the actual number of threads that
|
| 406 |
+
HMMER spawns is at least
|
| 407 |
+
.IR <n> +1.
|
| 408 |
+
|
| 409 |
+
This option is not available if HMMER was compiled with POSIX threads
|
| 410 |
+
support turned off.
|
| 411 |
+
|
| 412 |
+
|
| 413 |
+
|
| 414 |
+
|
| 415 |
+
.TP
|
| 416 |
+
.BI \-\-stall
|
| 417 |
+
For debugging the MPI master/worker version: pause after start, to
|
| 418 |
+
enable the developer to attach debuggers to the running master and
|
| 419 |
+
worker(s) processes. Send SIGCONT signal to release the pause.
|
| 420 |
+
(Under gdb:
|
| 421 |
+
.BR "(gdb) signal SIGCONT" )
|
| 422 |
+
|
| 423 |
+
(Only available if optional MPI support was enabled at compile-time.)
|
| 424 |
+
|
| 425 |
+
.TP
|
| 426 |
+
.BI \-\-mpi
|
| 427 |
+
Run under MPI control with master/worker parallelization (using
|
| 428 |
+
.BR mpirun ,
|
| 429 |
+
for example, or equivalent). Only available if optional MPI support
|
| 430 |
+
was enabled at compile-time.
|
| 431 |
+
|
| 432 |
+
|
| 433 |
+
|
| 434 |
+
|
| 435 |
+
|
| 436 |
+
|
| 437 |
+
|
| 438 |
+
|
| 439 |
+
|
| 440 |
+
.SH SEE ALSO
|
| 441 |
+
|
| 442 |
+
See
|
| 443 |
+
.BR hmmer (1)
|
| 444 |
+
for a master man page with a list of all the individual man pages
|
| 445 |
+
for programs in the HMMER package.
|
| 446 |
+
|
| 447 |
+
.PP
|
| 448 |
+
For complete documentation, see the user guide that came with your
|
| 449 |
+
HMMER distribution (Userguide.pdf); or see the HMMER web page
|
| 450 |
+
(http://hmmer.org/).
|
| 451 |
+
|
| 452 |
+
|
| 453 |
+
|
| 454 |
+
.SH COPYRIGHT
|
| 455 |
+
|
| 456 |
+
.nf
|
| 457 |
+
Copyright (C) 2023 Howard Hughes Medical Institute.
|
| 458 |
+
Freely distributed under the BSD open source license.
|
| 459 |
+
.fi
|
| 460 |
+
|
| 461 |
+
For additional information on copyright and licensing, see the file
|
| 462 |
+
called COPYRIGHT in your HMMER source distribution, or see the HMMER
|
| 463 |
+
web page
|
| 464 |
+
(http://hmmer.org/).
|
| 465 |
+
|
| 466 |
+
|
| 467 |
+
.SH AUTHOR
|
| 468 |
+
|
| 469 |
+
.nf
|
| 470 |
+
http://eddylab.org
|
| 471 |
+
.fi
|
| 472 |
+
|
flax_model/alphafold3/data/cpp/__init__.py
ADDED
|
File without changes
|
flax_model/alphafold3/data/cpp/msa_profile_pybind.cc
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
// Copyright 2024 DeepMind Technologies Limited
|
| 2 |
+
//
|
| 3 |
+
// AlphaFold 3 source code is licensed under CC BY-NC-SA 4.0. To view a copy of
|
| 4 |
+
// this license, visit https://creativecommons.org/licenses/by-nc-sa/4.0/
|
| 5 |
+
//
|
| 6 |
+
// To request access to the AlphaFold 3 model parameters, follow the process set
|
| 7 |
+
// out at https://github.com/google-deepmind/alphafold3. You may only use these
|
| 8 |
+
// if received directly from Google. Use is subject to terms of use available at
|
| 9 |
+
// https://github.com/google-deepmind/alphafold3/blob/main/WEIGHTS_TERMS_OF_USE.md
|
| 10 |
+
|
| 11 |
+
#include <algorithm>
|
| 12 |
+
|
| 13 |
+
#include "absl/strings/str_cat.h"
|
| 14 |
+
#include "pybind11/cast.h"
|
| 15 |
+
#include "pybind11/numpy.h"
|
| 16 |
+
#include "pybind11/pybind11.h"
|
| 17 |
+
|
| 18 |
+
namespace {
|
| 19 |
+
|
| 20 |
+
namespace py = pybind11;
|
| 21 |
+
|
| 22 |
+
py::array_t<double> ComputeMsaProfile(
|
| 23 |
+
const py::array_t<int, py::array::c_style>& msa, int num_residue_types) {
|
| 24 |
+
if (msa.size() == 0) {
|
| 25 |
+
throw py::value_error("The MSA must be non-empty.");
|
| 26 |
+
}
|
| 27 |
+
if (msa.ndim() != 2) {
|
| 28 |
+
throw py::value_error(absl::StrCat("The MSA must be rectangular, got ",
|
| 29 |
+
msa.ndim(), "-dimensional MSA array."));
|
| 30 |
+
}
|
| 31 |
+
const int msa_depth = msa.shape()[0];
|
| 32 |
+
const int sequence_length = msa.shape()[1];
|
| 33 |
+
|
| 34 |
+
py::array_t<double> profile({sequence_length, num_residue_types});
|
| 35 |
+
std::fill(profile.mutable_data(), profile.mutable_data() + profile.size(),
|
| 36 |
+
0.0f);
|
| 37 |
+
auto profile_unchecked = profile.mutable_unchecked<2>();
|
| 38 |
+
|
| 39 |
+
const double normalized_count = 1.0 / msa_depth;
|
| 40 |
+
const int* msa_it = msa.data();
|
| 41 |
+
for (int row_index = 0; row_index < msa_depth; ++row_index) {
|
| 42 |
+
for (int column_index = 0; column_index < sequence_length; ++column_index) {
|
| 43 |
+
const int residue_code = *(msa_it++);
|
| 44 |
+
if (residue_code < 0 || residue_code >= num_residue_types) {
|
| 45 |
+
throw py::value_error(
|
| 46 |
+
absl::StrCat("All residue codes must be positive and smaller than "
|
| 47 |
+
"num_residue_types ",
|
| 48 |
+
num_residue_types, ", got ", residue_code));
|
| 49 |
+
}
|
| 50 |
+
profile_unchecked(column_index, residue_code) += normalized_count;
|
| 51 |
+
}
|
| 52 |
+
}
|
| 53 |
+
return profile;
|
| 54 |
+
}
|
| 55 |
+
|
| 56 |
+
constexpr char kComputeMsaProfileDoc[] = R"(
|
| 57 |
+
Computes MSA profile for the given encoded MSA.
|
| 58 |
+
|
| 59 |
+
Args:
|
| 60 |
+
msa: A Numpy array of shape (num_msa, num_res) with the integer coded MSA.
|
| 61 |
+
num_residue_types: Integer that determines the number of unique residue types.
|
| 62 |
+
This will determine the shape of the output profile.
|
| 63 |
+
|
| 64 |
+
Returns:
|
| 65 |
+
A float Numpy array of shape (num_res, num_residue_types) with residue
|
| 66 |
+
frequency (residue type count normalized by MSA depth) for every column of the
|
| 67 |
+
MSA.
|
| 68 |
+
)";
|
| 69 |
+
|
| 70 |
+
} // namespace
|
| 71 |
+
|
| 72 |
+
namespace alphafold3 {
|
| 73 |
+
|
| 74 |
+
void RegisterModuleMsaProfile(pybind11::module m) {
|
| 75 |
+
m.def("compute_msa_profile", &ComputeMsaProfile, py::arg("msa"),
|
| 76 |
+
py::arg("num_residue_types"), py::doc(kComputeMsaProfileDoc + 1));
|
| 77 |
+
}
|
| 78 |
+
|
| 79 |
+
} // namespace alphafold3
|
flax_model/alphafold3/data/msa.py
ADDED
|
@@ -0,0 +1,348 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
|
| 2 |
+
|
| 3 |
+
"""Functions for getting MSA and calculating alignment features."""
|
| 4 |
+
|
| 5 |
+
from collections.abc import MutableMapping, Sequence
|
| 6 |
+
import string
|
| 7 |
+
from typing import Self
|
| 8 |
+
|
| 9 |
+
from absl import logging
|
| 10 |
+
from flax_model.alphafold3.constants import mmcif_names
|
| 11 |
+
from flax_model.alphafold3.data import msa_config
|
| 12 |
+
from flax_model.alphafold3.data import msa_features
|
| 13 |
+
from flax_model.alphafold3.data import parsers
|
| 14 |
+
from flax_model.alphafold3.data.tools import jackhmmer
|
| 15 |
+
from flax_model.alphafold3.data.tools import msa_tool
|
| 16 |
+
from flax_model.alphafold3.data.tools import nhmmer
|
| 17 |
+
from flax_model.alphafold3.data.tools import mmseqs
|
| 18 |
+
import numpy as np
|
| 19 |
+
|
| 20 |
+
|
| 21 |
+
|
| 22 |
+
class Error(Exception):
|
| 23 |
+
"""Error indicatating a problem with MSA Search."""
|
| 24 |
+
|
| 25 |
+
|
| 26 |
+
def _featurize(seq: str, chain_poly_type: str) -> str | list[int]:
|
| 27 |
+
if mmcif_names.is_standard_polymer_type(chain_poly_type):
|
| 28 |
+
featurized_seqs, _ = msa_features.extract_msa_features(
|
| 29 |
+
msa_sequences=[seq], chain_poly_type=chain_poly_type
|
| 30 |
+
)
|
| 31 |
+
return featurized_seqs[0].tolist()
|
| 32 |
+
# For anything else simply require an identical match.
|
| 33 |
+
return seq
|
| 34 |
+
|
| 35 |
+
|
| 36 |
+
def sequences_are_feature_equivalent(
|
| 37 |
+
sequence1: str,
|
| 38 |
+
sequence2: str,
|
| 39 |
+
chain_poly_type: str,
|
| 40 |
+
) -> bool:
|
| 41 |
+
feat1 = _featurize(sequence1, chain_poly_type)
|
| 42 |
+
feat2 = _featurize(sequence2, chain_poly_type)
|
| 43 |
+
return feat1 == feat2
|
| 44 |
+
|
| 45 |
+
|
| 46 |
+
class Msa:
|
| 47 |
+
"""Multiple Sequence Alignment container with methods for manipulating it."""
|
| 48 |
+
|
| 49 |
+
def __init__(
|
| 50 |
+
self,
|
| 51 |
+
query_sequence: str,
|
| 52 |
+
chain_poly_type: str,
|
| 53 |
+
sequences: Sequence[str],
|
| 54 |
+
descriptions: Sequence[str],
|
| 55 |
+
deduplicate: bool = True,
|
| 56 |
+
):
|
| 57 |
+
"""Raw constructor, prefer using the from_{a3m,multiple_msas} class methods.
|
| 58 |
+
|
| 59 |
+
The first sequence must be equal (in featurised form) to the query sequence.
|
| 60 |
+
If sequences/descriptions are empty, they will be initialised to the query.
|
| 61 |
+
|
| 62 |
+
Args:
|
| 63 |
+
query_sequence: The sequence that was used to search for MSA.
|
| 64 |
+
chain_poly_type: Polymer type of the query sequence, see mmcif_names.
|
| 65 |
+
sequences: The sequences returned by the MSA search tool.
|
| 66 |
+
descriptions: Metadata for the sequences returned by the MSA search tool.
|
| 67 |
+
deduplicate: If True, the MSA sequences will be deduplicated in the input
|
| 68 |
+
order. Lowercase letters (insertions) are ignored when deduplicating.
|
| 69 |
+
"""
|
| 70 |
+
if len(sequences) != len(descriptions):
|
| 71 |
+
raise ValueError('The number of sequences and descriptions must match.')
|
| 72 |
+
|
| 73 |
+
self.query_sequence = query_sequence
|
| 74 |
+
self.chain_poly_type = chain_poly_type
|
| 75 |
+
|
| 76 |
+
if not deduplicate:
|
| 77 |
+
self.sequences = sequences
|
| 78 |
+
self.descriptions = descriptions
|
| 79 |
+
else:
|
| 80 |
+
self.sequences = []
|
| 81 |
+
self.descriptions = []
|
| 82 |
+
# A replacement table that removes all lowercase characters.
|
| 83 |
+
deletion_table = str.maketrans('', '', string.ascii_lowercase)
|
| 84 |
+
unique_sequences = set()
|
| 85 |
+
for seq, desc in zip(sequences, descriptions, strict=True):
|
| 86 |
+
# Using string.translate is faster than re.sub('[a-z]+', '').
|
| 87 |
+
sequence_no_deletions = seq.translate(deletion_table)
|
| 88 |
+
if sequence_no_deletions not in unique_sequences:
|
| 89 |
+
unique_sequences.add(sequence_no_deletions)
|
| 90 |
+
self.sequences.append(seq)
|
| 91 |
+
self.descriptions.append(desc)
|
| 92 |
+
|
| 93 |
+
# Make sure the MSA always has at least the query.
|
| 94 |
+
self.sequences = self.sequences or [query_sequence]
|
| 95 |
+
self.descriptions = self.descriptions or ['Original query']
|
| 96 |
+
|
| 97 |
+
# Check if the 1st MSA sequence matches the query sequence. Since it may be
|
| 98 |
+
# mutated by the search tool (jackhmmer) check using the featurized version.
|
| 99 |
+
if not sequences_are_feature_equivalent(
|
| 100 |
+
self.sequences[0], query_sequence, chain_poly_type
|
| 101 |
+
):
|
| 102 |
+
raise ValueError(
|
| 103 |
+
f'First MSA sequence {self.sequences[0]} is not the {query_sequence=}'
|
| 104 |
+
)
|
| 105 |
+
|
| 106 |
+
@classmethod
|
| 107 |
+
def from_multiple_msas(
|
| 108 |
+
cls, msas: Sequence[Self], deduplicate: bool = True
|
| 109 |
+
) -> Self:
|
| 110 |
+
"""Initializes the MSA from multiple MSAs.
|
| 111 |
+
|
| 112 |
+
Args:
|
| 113 |
+
msas: A sequence of Msa objects representing individual MSAs produced by
|
| 114 |
+
different tools/dbs.
|
| 115 |
+
deduplicate: If True, the MSA sequences will be deduplicated in the input
|
| 116 |
+
order. Lowercase letters (insertions) are ignored when deduplicating.
|
| 117 |
+
|
| 118 |
+
Returns:
|
| 119 |
+
An Msa object created by merging multiple MSAs.
|
| 120 |
+
"""
|
| 121 |
+
if not msas:
|
| 122 |
+
raise ValueError('At least one MSA must be provided.')
|
| 123 |
+
|
| 124 |
+
query_sequence = msas[0].query_sequence
|
| 125 |
+
chain_poly_type = msas[0].chain_poly_type
|
| 126 |
+
sequences = []
|
| 127 |
+
descriptions = []
|
| 128 |
+
|
| 129 |
+
for msa in msas:
|
| 130 |
+
if msa.query_sequence != query_sequence:
|
| 131 |
+
raise ValueError(
|
| 132 |
+
f'Query sequences must match: {[m.query_sequence for m in msas]}'
|
| 133 |
+
)
|
| 134 |
+
if msa.chain_poly_type != chain_poly_type:
|
| 135 |
+
raise ValueError(
|
| 136 |
+
f'Chain poly types must match: {[m.chain_poly_type for m in msas]}'
|
| 137 |
+
)
|
| 138 |
+
sequences.extend(msa.sequences)
|
| 139 |
+
descriptions.extend(msa.descriptions)
|
| 140 |
+
|
| 141 |
+
return cls(
|
| 142 |
+
query_sequence=query_sequence,
|
| 143 |
+
chain_poly_type=chain_poly_type,
|
| 144 |
+
sequences=sequences,
|
| 145 |
+
descriptions=descriptions,
|
| 146 |
+
deduplicate=deduplicate,
|
| 147 |
+
)
|
| 148 |
+
|
| 149 |
+
@classmethod
|
| 150 |
+
def from_multiple_a3ms(
|
| 151 |
+
cls, a3ms: Sequence[str], chain_poly_type: str, deduplicate: bool = True
|
| 152 |
+
) -> Self:
|
| 153 |
+
"""Initializes the MSA from multiple A3M strings.
|
| 154 |
+
|
| 155 |
+
Args:
|
| 156 |
+
a3ms: A sequence of A3M strings representing individual MSAs produced by
|
| 157 |
+
different tools/dbs.
|
| 158 |
+
chain_poly_type: Polymer type of the query sequence, see mmcif_names.
|
| 159 |
+
deduplicate: If True, the MSA sequences will be deduplicated in the input
|
| 160 |
+
order. Lowercase letters (insertions) are ignored when deduplicating.
|
| 161 |
+
|
| 162 |
+
Returns:
|
| 163 |
+
An Msa object created by merging multiple A3Ms.
|
| 164 |
+
"""
|
| 165 |
+
if not a3ms:
|
| 166 |
+
raise ValueError('At least one A3M must be provided.')
|
| 167 |
+
|
| 168 |
+
query_sequence = None
|
| 169 |
+
all_sequences = []
|
| 170 |
+
all_descriptions = []
|
| 171 |
+
|
| 172 |
+
for a3m in a3ms:
|
| 173 |
+
sequences, descriptions = parsers.parse_fasta(a3m)
|
| 174 |
+
if query_sequence is None:
|
| 175 |
+
query_sequence = sequences[0]
|
| 176 |
+
|
| 177 |
+
if sequences[0] != query_sequence:
|
| 178 |
+
raise ValueError(
|
| 179 |
+
f'Query sequences must match: {sequences[0]=} != {query_sequence=}'
|
| 180 |
+
)
|
| 181 |
+
all_sequences.extend(sequences)
|
| 182 |
+
all_descriptions.extend(descriptions)
|
| 183 |
+
|
| 184 |
+
return cls(
|
| 185 |
+
query_sequence=query_sequence,
|
| 186 |
+
chain_poly_type=chain_poly_type,
|
| 187 |
+
sequences=all_sequences,
|
| 188 |
+
descriptions=all_descriptions,
|
| 189 |
+
deduplicate=deduplicate,
|
| 190 |
+
)
|
| 191 |
+
|
| 192 |
+
@classmethod
|
| 193 |
+
def from_a3m(
|
| 194 |
+
cls,
|
| 195 |
+
query_sequence: str,
|
| 196 |
+
chain_poly_type: str,
|
| 197 |
+
a3m: str,
|
| 198 |
+
max_depth: int | None = None,
|
| 199 |
+
deduplicate: bool = True,
|
| 200 |
+
) -> Self:
|
| 201 |
+
"""Parses the single A3M and builds the Msa object."""
|
| 202 |
+
sequences, descriptions = parsers.parse_fasta(a3m)
|
| 203 |
+
|
| 204 |
+
if max_depth is not None and 0 < max_depth < len(sequences):
|
| 205 |
+
logging.info(
|
| 206 |
+
'MSA cropped from depth of %d to %d for %s.',
|
| 207 |
+
len(sequences),
|
| 208 |
+
max_depth,
|
| 209 |
+
query_sequence,
|
| 210 |
+
)
|
| 211 |
+
sequences = sequences[:max_depth]
|
| 212 |
+
descriptions = descriptions[:max_depth]
|
| 213 |
+
|
| 214 |
+
return cls(
|
| 215 |
+
query_sequence=query_sequence,
|
| 216 |
+
chain_poly_type=chain_poly_type,
|
| 217 |
+
sequences=sequences,
|
| 218 |
+
descriptions=descriptions,
|
| 219 |
+
deduplicate=deduplicate,
|
| 220 |
+
)
|
| 221 |
+
|
| 222 |
+
@classmethod
|
| 223 |
+
def from_empty(cls, query_sequence: str, chain_poly_type: str) -> Self:
|
| 224 |
+
"""Creates an empty Msa containing just the query sequence."""
|
| 225 |
+
return cls(
|
| 226 |
+
query_sequence=query_sequence,
|
| 227 |
+
chain_poly_type=chain_poly_type,
|
| 228 |
+
sequences=[],
|
| 229 |
+
descriptions=[],
|
| 230 |
+
deduplicate=False,
|
| 231 |
+
)
|
| 232 |
+
|
| 233 |
+
@property
|
| 234 |
+
def depth(self) -> int:
|
| 235 |
+
return len(self.sequences)
|
| 236 |
+
|
| 237 |
+
def __repr__(self) -> str:
|
| 238 |
+
return f'Msa({self.depth} sequences, {self.chain_poly_type})'
|
| 239 |
+
|
| 240 |
+
def to_a3m(self) -> str:
|
| 241 |
+
"""Returns the MSA in the A3M format."""
|
| 242 |
+
a3m_lines = []
|
| 243 |
+
for desc, seq in zip(self.descriptions, self.sequences, strict=True):
|
| 244 |
+
a3m_lines.append(f'>{desc}')
|
| 245 |
+
a3m_lines.append(seq)
|
| 246 |
+
return '\n'.join(a3m_lines) + '\n'
|
| 247 |
+
|
| 248 |
+
def featurize(self) -> MutableMapping[str, np.ndarray]:
|
| 249 |
+
"""Featurises the MSA and returns a map of feature names to features.
|
| 250 |
+
|
| 251 |
+
Returns:
|
| 252 |
+
A dictionary mapping feature names to values.
|
| 253 |
+
|
| 254 |
+
Raises:
|
| 255 |
+
msa.Error:
|
| 256 |
+
* If the sequences in the MSA don't have the same length after deletions
|
| 257 |
+
(lower case letters) are removed.
|
| 258 |
+
* If the MSA contains an unknown amino acid code.
|
| 259 |
+
* If there are no sequences after aligning.
|
| 260 |
+
"""
|
| 261 |
+
try:
|
| 262 |
+
msa, deletion_matrix = msa_features.extract_msa_features(
|
| 263 |
+
msa_sequences=self.sequences, chain_poly_type=self.chain_poly_type
|
| 264 |
+
)
|
| 265 |
+
except ValueError as e:
|
| 266 |
+
raise Error(f'Error extracting MSA or deletion features: {e}') from e
|
| 267 |
+
|
| 268 |
+
if msa.shape == (0, 0):
|
| 269 |
+
raise Error(f'Empty MSA feature for {self}')
|
| 270 |
+
|
| 271 |
+
species_ids = msa_features.extract_species_ids(self.descriptions)
|
| 272 |
+
|
| 273 |
+
return {
|
| 274 |
+
'msa_species_identifiers': np.array(species_ids, dtype=object),
|
| 275 |
+
'num_alignments': np.array(self.depth, dtype=np.int32),
|
| 276 |
+
'msa': msa,
|
| 277 |
+
'deletion_matrix': deletion_matrix,
|
| 278 |
+
}
|
| 279 |
+
|
| 280 |
+
|
| 281 |
+
def get_msa_tool(
|
| 282 |
+
msa_tool_config: msa_config.JackhmmerConfig | msa_config.NhmmerConfig | msa_config.MmseqsConfig,
|
| 283 |
+
) -> msa_tool.MsaTool:
|
| 284 |
+
"""Returns the requested MSA tool."""
|
| 285 |
+
|
| 286 |
+
match msa_tool_config:
|
| 287 |
+
case msa_config.JackhmmerConfig():
|
| 288 |
+
return jackhmmer.Jackhmmer(
|
| 289 |
+
binary_path=msa_tool_config.binary_path,
|
| 290 |
+
database_path=msa_tool_config.database_config.path,
|
| 291 |
+
n_cpu=msa_tool_config.n_cpu,
|
| 292 |
+
n_iter=msa_tool_config.n_iter,
|
| 293 |
+
e_value=msa_tool_config.e_value,
|
| 294 |
+
z_value=msa_tool_config.z_value,
|
| 295 |
+
max_sequences=msa_tool_config.max_sequences,
|
| 296 |
+
max_threads=msa_tool_config.max_threads,
|
| 297 |
+
)
|
| 298 |
+
case msa_config.NhmmerConfig():
|
| 299 |
+
return nhmmer.Nhmmer(
|
| 300 |
+
binary_path=msa_tool_config.binary_path,
|
| 301 |
+
hmmalign_binary_path=msa_tool_config.hmmalign_binary_path,
|
| 302 |
+
hmmbuild_binary_path=msa_tool_config.hmmbuild_binary_path,
|
| 303 |
+
database_path=msa_tool_config.database_config.path,
|
| 304 |
+
n_cpu=msa_tool_config.n_cpu,
|
| 305 |
+
e_value=msa_tool_config.e_value,
|
| 306 |
+
max_sequences=msa_tool_config.max_sequences,
|
| 307 |
+
max_threads=msa_tool_config.max_threads,
|
| 308 |
+
alphabet=msa_tool_config.alphabet,
|
| 309 |
+
)
|
| 310 |
+
case msa_config.MmseqsConfig():
|
| 311 |
+
return mmseqs.Mmseqs(
|
| 312 |
+
binary_path=msa_tool_config.binary_path,
|
| 313 |
+
database_path=msa_tool_config.database_config.path,
|
| 314 |
+
n_cpu=msa_tool_config.n_cpu,
|
| 315 |
+
use_gpu=msa_tool_config.use_gpu,
|
| 316 |
+
mmseqs_options=msa_tool_config.mmseqs_options,
|
| 317 |
+
result2msa_options=msa_tool_config.result2msa_options,
|
| 318 |
+
)
|
| 319 |
+
case _:
|
| 320 |
+
raise ValueError(f'Unknown MSA tool: {msa_tool_config}.')
|
| 321 |
+
|
| 322 |
+
|
| 323 |
+
def get_msa(
|
| 324 |
+
target_sequence: str,
|
| 325 |
+
run_config: msa_config.RunConfig,
|
| 326 |
+
chain_poly_type: str,
|
| 327 |
+
deduplicate: bool = False,
|
| 328 |
+
) -> Msa:
|
| 329 |
+
"""Computes the MSA for a given query sequence.
|
| 330 |
+
|
| 331 |
+
Args:
|
| 332 |
+
target_sequence: The target amino-acid sequence.
|
| 333 |
+
run_config: MSA run configuration.
|
| 334 |
+
chain_poly_type: The type of chain for which to get an MSA.
|
| 335 |
+
deduplicate: If True, the MSA sequences will be deduplicated in the input
|
| 336 |
+
order. Lowercase letters (insertions) are ignored when deduplicating.
|
| 337 |
+
|
| 338 |
+
Returns:
|
| 339 |
+
Aligned MSA sequences.
|
| 340 |
+
"""
|
| 341 |
+
|
| 342 |
+
return Msa.from_a3m(
|
| 343 |
+
query_sequence=target_sequence,
|
| 344 |
+
chain_poly_type=chain_poly_type,
|
| 345 |
+
a3m=get_msa_tool(run_config.config).query(target_sequence).a3m,
|
| 346 |
+
max_depth=run_config.crop_size,
|
| 347 |
+
deduplicate=deduplicate,
|
| 348 |
+
)
|
flax_model/alphafold3/data/msa_store.py
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
|
| 2 |
+
|
| 3 |
+
"""Interface and implementations for fetching MSA data."""
|
| 4 |
+
|
| 5 |
+
from collections.abc import Sequence
|
| 6 |
+
from typing import Protocol, TypeAlias
|
| 7 |
+
|
| 8 |
+
from flax_model.alphafold3.data import msa
|
| 9 |
+
from flax_model.alphafold3.data import msa_config
|
| 10 |
+
|
| 11 |
+
|
| 12 |
+
MsaErrors: TypeAlias = Sequence[tuple[msa_config.RunConfig, str]]
|
| 13 |
+
|
| 14 |
+
|
| 15 |
+
class MsaProvider(Protocol):
|
| 16 |
+
"""Interface for providing Multiple Sequence Alignments."""
|
| 17 |
+
|
| 18 |
+
def __call__(
|
| 19 |
+
self,
|
| 20 |
+
query_sequence: str,
|
| 21 |
+
chain_polymer_type: str,
|
| 22 |
+
) -> tuple[msa.Msa, MsaErrors]:
|
| 23 |
+
"""Retrieve MSA for the given polymer query_sequence.
|
| 24 |
+
|
| 25 |
+
Args:
|
| 26 |
+
query_sequence: The residue sequence of the polymer to search for.
|
| 27 |
+
chain_polymer_type: The polymer type of the query_sequence. This must
|
| 28 |
+
match the chain_polymer_type of the provider.
|
| 29 |
+
|
| 30 |
+
Returns:
|
| 31 |
+
A tuple containing the MSA and MsaErrors. MsaErrors is a Sequence
|
| 32 |
+
containing a tuple for each msa_query that failed. Each tuple contains
|
| 33 |
+
the failing query and the associated error message.
|
| 34 |
+
"""
|
| 35 |
+
|
| 36 |
+
|
| 37 |
+
class EmptyMsaProvider:
|
| 38 |
+
"""MSA provider that returns just the query sequence, useful for testing."""
|
| 39 |
+
|
| 40 |
+
def __init__(self, chain_polymer_type: str):
|
| 41 |
+
self._chain_polymer_type = chain_polymer_type
|
| 42 |
+
|
| 43 |
+
def __call__(
|
| 44 |
+
self, query_sequence: str, chain_polymer_type: str
|
| 45 |
+
) -> tuple[msa.Msa, MsaErrors]:
|
| 46 |
+
"""Returns an MSA containing just the query sequence, never errors."""
|
| 47 |
+
if chain_polymer_type != self._chain_polymer_type:
|
| 48 |
+
raise ValueError(
|
| 49 |
+
f'EmptyMsaProvider of type {self._chain_polymer_type} called with '
|
| 50 |
+
f'sequence of {chain_polymer_type=}, {query_sequence=}.'
|
| 51 |
+
)
|
| 52 |
+
return (
|
| 53 |
+
msa.Msa.from_empty(
|
| 54 |
+
query_sequence=query_sequence,
|
| 55 |
+
chain_poly_type=self._chain_polymer_type,
|
| 56 |
+
),
|
| 57 |
+
(),
|
| 58 |
+
)
|
flax_model/alphafold3/data/template_realign.py
ADDED
|
@@ -0,0 +1,160 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
|
| 2 |
+
|
| 3 |
+
"""Realign sequences found in PDB seqres to the actual CIF sequences."""
|
| 4 |
+
|
| 5 |
+
from collections.abc import Mapping
|
| 6 |
+
|
| 7 |
+
|
| 8 |
+
class AlignmentError(Exception):
|
| 9 |
+
"""Failed alignment between the hit sequence and the actual mmCIF sequence."""
|
| 10 |
+
|
| 11 |
+
|
| 12 |
+
def realign_hit_to_structure(
|
| 13 |
+
*,
|
| 14 |
+
hit_sequence: str,
|
| 15 |
+
hit_start_index: int,
|
| 16 |
+
hit_end_index: int,
|
| 17 |
+
full_length: int,
|
| 18 |
+
structure_sequence: str,
|
| 19 |
+
query_to_hit_mapping: Mapping[int, int],
|
| 20 |
+
) -> Mapping[int, int]:
|
| 21 |
+
"""Realigns the hit sequence to the Structure sequence.
|
| 22 |
+
|
| 23 |
+
For example, for the given input:
|
| 24 |
+
query_sequence : ABCDEFGHIJKL
|
| 25 |
+
hit_sequence : ---DEFGHIJK-
|
| 26 |
+
struc_sequence : XDEFGHKL
|
| 27 |
+
the mapping is {3: 0, 4: 1, 5: 2, 6: 3, 7: 4, 8: 5, 9: 6, 10: 7}. However, the
|
| 28 |
+
actual Structure sequence has an extra X at the start as well as no IJ. So the
|
| 29 |
+
alignment from the query to the Structure sequence will be:
|
| 30 |
+
hit_sequence : ---DEFGHIJK-
|
| 31 |
+
struc_aligned : --XDEFGH--KL
|
| 32 |
+
and the new mapping will therefore be: {3: 1, 4: 2, 5: 3, 6: 4, 7: 5, 10: 6}.
|
| 33 |
+
|
| 34 |
+
Args:
|
| 35 |
+
hit_sequence: The PDB seqres hit sequence obtained from Hmmsearch, but
|
| 36 |
+
without any gaps. This is not the full PDB seqres template sequence but
|
| 37 |
+
rather just its subsequence from hit_start_index to hit_end_index.
|
| 38 |
+
hit_start_index: The start index of the hit sequence in the full PDB seqres
|
| 39 |
+
template sequence (inclusive).
|
| 40 |
+
hit_end_index: The end index of the hit sequence in the full PDB seqres
|
| 41 |
+
template sequence (exclusive).
|
| 42 |
+
full_length: The length of the full PDB seqres template sequence.
|
| 43 |
+
structure_sequence: The actual sequence extracted from the Structure
|
| 44 |
+
corresponding to this template. In vast majority of cases this is the same
|
| 45 |
+
as the PDB seqres sequence, but this function handles the cases when not.
|
| 46 |
+
query_to_hit_mapping: The mapping from the query sequence to the
|
| 47 |
+
hit_sequence.
|
| 48 |
+
|
| 49 |
+
Raises:
|
| 50 |
+
AlignmentError: if the alignment between the sequence returned by Hmmsearch
|
| 51 |
+
differs from the actual sequence found in the mmCIF and can't be aligned
|
| 52 |
+
using the simple alignment algorithm.
|
| 53 |
+
|
| 54 |
+
Returns:
|
| 55 |
+
A mapping from the query sequence to the actual Structure sequence.
|
| 56 |
+
"""
|
| 57 |
+
max_num_gaps = full_length - len(structure_sequence)
|
| 58 |
+
if max_num_gaps < 0:
|
| 59 |
+
raise AlignmentError(
|
| 60 |
+
f'The Structure sequence ({len(structure_sequence)}) '
|
| 61 |
+
f'must be shorter than the PDB seqres sequence ({full_length}):\n'
|
| 62 |
+
f'Structure sequence : {structure_sequence}\n'
|
| 63 |
+
f'PDB seqres sequence: {hit_sequence}'
|
| 64 |
+
)
|
| 65 |
+
|
| 66 |
+
if len(hit_sequence) != hit_end_index - hit_start_index:
|
| 67 |
+
raise AlignmentError(
|
| 68 |
+
f'The difference of {hit_end_index=} and {hit_start_index=} does not '
|
| 69 |
+
f'equal to the length of the {hit_sequence}: {len(hit_sequence)}'
|
| 70 |
+
)
|
| 71 |
+
|
| 72 |
+
best_score = -1
|
| 73 |
+
best_start = 0
|
| 74 |
+
best_query_to_hit_mapping = query_to_hit_mapping
|
| 75 |
+
max_num_gaps_before_subseq = min(hit_start_index, max_num_gaps)
|
| 76 |
+
# It is possible the gaps needed to align the PDB seqres subsequence and
|
| 77 |
+
# the Structure subsequence need to be inserted before the match region.
|
| 78 |
+
# Try and pick the alignment with the best number of aligned residues.
|
| 79 |
+
for num_gaps_before_subseq in range(0, max_num_gaps_before_subseq + 1):
|
| 80 |
+
start = hit_start_index - num_gaps_before_subseq
|
| 81 |
+
end = hit_end_index - num_gaps_before_subseq
|
| 82 |
+
structure_subseq = structure_sequence[start:end]
|
| 83 |
+
|
| 84 |
+
new_query_to_hit_mapping, score = _remap_to_struc_seq(
|
| 85 |
+
hit_seq=hit_sequence,
|
| 86 |
+
struc_seq=structure_subseq,
|
| 87 |
+
max_num_gaps=max_num_gaps - num_gaps_before_subseq,
|
| 88 |
+
mapping=query_to_hit_mapping,
|
| 89 |
+
)
|
| 90 |
+
if score >= best_score:
|
| 91 |
+
# Use >= to prefer matches with larger number of gaps before.
|
| 92 |
+
best_score = score
|
| 93 |
+
best_start = start
|
| 94 |
+
best_query_to_hit_mapping = new_query_to_hit_mapping
|
| 95 |
+
|
| 96 |
+
return {q: h + best_start for q, h in best_query_to_hit_mapping.items()}
|
| 97 |
+
|
| 98 |
+
|
| 99 |
+
def _remap_to_struc_seq(
|
| 100 |
+
*,
|
| 101 |
+
hit_seq: str,
|
| 102 |
+
struc_seq: str,
|
| 103 |
+
max_num_gaps: int,
|
| 104 |
+
mapping: Mapping[int, int],
|
| 105 |
+
) -> tuple[Mapping[int, int], int]:
|
| 106 |
+
"""Remaps the query -> hit mapping to match the actual Structure sequence.
|
| 107 |
+
|
| 108 |
+
Args:
|
| 109 |
+
hit_seq: The hit sequence - a subsequence of the PDB seqres sequence without
|
| 110 |
+
any Hmmsearch modifications like inserted gaps or lowercased residues.
|
| 111 |
+
struc_seq: The actual sequence obtained from the corresponding Structure.
|
| 112 |
+
max_num_gaps: The maximum number of gaps that can be inserted in the
|
| 113 |
+
Structure sequence. In practice, this is the length difference between the
|
| 114 |
+
PDB seqres sequence and the actual Structure sequence.
|
| 115 |
+
mapping: The mapping from the query residues to the hit residues. This will
|
| 116 |
+
be remapped to point to the actual Structure sequence using a simple
|
| 117 |
+
realignment algorithm.
|
| 118 |
+
|
| 119 |
+
Returns:
|
| 120 |
+
A tuple of (mapping, score):
|
| 121 |
+
* Mapping from the query to the actual Structure sequence.
|
| 122 |
+
* Score which is the number of matching aligned residues.
|
| 123 |
+
|
| 124 |
+
Raises:
|
| 125 |
+
ValueError if the structure sequence isn't shorter than the seqres sequence.
|
| 126 |
+
ValueError if the alignment fails.
|
| 127 |
+
"""
|
| 128 |
+
hit_seq_idx = 0
|
| 129 |
+
struc_seq_idx = 0
|
| 130 |
+
hit_to_struc_seq_mapping = {}
|
| 131 |
+
score = 0
|
| 132 |
+
|
| 133 |
+
# This while loop is guaranteed to terminate since we increase both
|
| 134 |
+
# struc_seq_idx and hit_seq_idx by at least 1 in each iteration.
|
| 135 |
+
remaining_num_gaps = max_num_gaps
|
| 136 |
+
while hit_seq_idx < len(hit_seq) and struc_seq_idx < len(struc_seq):
|
| 137 |
+
if hit_seq[hit_seq_idx] != struc_seq[struc_seq_idx]:
|
| 138 |
+
# Explore which alignment aligns the next residue (if present).
|
| 139 |
+
best_shift = 0
|
| 140 |
+
for shift in range(0, remaining_num_gaps + 1):
|
| 141 |
+
next_hit_res = hit_seq[hit_seq_idx + shift : hit_seq_idx + shift + 1]
|
| 142 |
+
next_struc_res = struc_seq[struc_seq_idx : struc_seq_idx + 1]
|
| 143 |
+
if next_hit_res == next_struc_res:
|
| 144 |
+
best_shift = shift
|
| 145 |
+
break
|
| 146 |
+
hit_seq_idx += best_shift
|
| 147 |
+
remaining_num_gaps -= best_shift
|
| 148 |
+
|
| 149 |
+
hit_to_struc_seq_mapping[hit_seq_idx] = struc_seq_idx
|
| 150 |
+
score += hit_seq[hit_seq_idx] == struc_seq[struc_seq_idx]
|
| 151 |
+
hit_seq_idx += 1
|
| 152 |
+
struc_seq_idx += 1
|
| 153 |
+
|
| 154 |
+
fixed_mapping = {}
|
| 155 |
+
for query_idx, original_hit_idx in mapping.items():
|
| 156 |
+
fixed_hit_idx = hit_to_struc_seq_mapping.get(original_hit_idx)
|
| 157 |
+
if fixed_hit_idx is not None:
|
| 158 |
+
fixed_mapping[query_idx] = fixed_hit_idx
|
| 159 |
+
|
| 160 |
+
return fixed_mapping, score
|
flax_model/alphafold3/data/templates.py
ADDED
|
@@ -0,0 +1,978 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
|
| 2 |
+
|
| 3 |
+
"""API for retrieving and manipulating template search results."""
|
| 4 |
+
|
| 5 |
+
from collections.abc import Iterable, Iterator, Mapping, Sequence
|
| 6 |
+
import dataclasses
|
| 7 |
+
import datetime
|
| 8 |
+
import functools
|
| 9 |
+
import os
|
| 10 |
+
import re
|
| 11 |
+
from typing import Any, Final, Self, TypeAlias
|
| 12 |
+
|
| 13 |
+
from absl import logging
|
| 14 |
+
from flax_model.alphafold3 import structure
|
| 15 |
+
from flax_model.alphafold3.common import resources
|
| 16 |
+
from flax_model.alphafold3.constants import atom_types
|
| 17 |
+
from flax_model.alphafold3.constants import mmcif_names
|
| 18 |
+
from flax_model.alphafold3.constants import residue_names
|
| 19 |
+
from flax_model.alphafold3.data import msa_config
|
| 20 |
+
from flax_model.alphafold3.data import parsers
|
| 21 |
+
from flax_model.alphafold3.data import structure_stores
|
| 22 |
+
from flax_model.alphafold3.data import template_realign
|
| 23 |
+
from flax_model.alphafold3.data.tools import hmmsearch
|
| 24 |
+
from flax_model.alphafold3.structure import mmcif
|
| 25 |
+
import numpy as np
|
| 26 |
+
|
| 27 |
+
|
| 28 |
+
_POLYMER_FEATURES: Final[Mapping[str, np.float64 | np.int32 | object]] = {
|
| 29 |
+
'template_aatype': np.int32,
|
| 30 |
+
'template_all_atom_masks': np.float64,
|
| 31 |
+
'template_all_atom_positions': np.float64,
|
| 32 |
+
'template_domain_names': object,
|
| 33 |
+
'template_release_date': object,
|
| 34 |
+
'template_sequence': object,
|
| 35 |
+
}
|
| 36 |
+
|
| 37 |
+
_LIGAND_FEATURES: Final[Mapping[str, Any]] = {
|
| 38 |
+
'ligand_features': Mapping[str, Any]
|
| 39 |
+
}
|
| 40 |
+
|
| 41 |
+
|
| 42 |
+
TemplateFeatures: TypeAlias = Mapping[
|
| 43 |
+
str, np.ndarray | bytes | Mapping[str, np.ndarray | bytes]
|
| 44 |
+
]
|
| 45 |
+
_REQUIRED_METADATA_COLUMNS: Final[Sequence[str]] = (
|
| 46 |
+
'seq_release_date',
|
| 47 |
+
'seq_unresolved_res_num',
|
| 48 |
+
'seq_author_chain_id',
|
| 49 |
+
'seq_sequence',
|
| 50 |
+
)
|
| 51 |
+
|
| 52 |
+
|
| 53 |
+
@dataclasses.dataclass(frozen=True, kw_only=True, slots=True)
|
| 54 |
+
class _Polymer:
|
| 55 |
+
"""Container for alphabet specific (dna, rna, protein) atom information."""
|
| 56 |
+
|
| 57 |
+
min_atoms: int
|
| 58 |
+
num_atom_types: int
|
| 59 |
+
atom_order: Mapping[str, int]
|
| 60 |
+
|
| 61 |
+
|
| 62 |
+
_POLYMERS = {
|
| 63 |
+
mmcif_names.PROTEIN_CHAIN: _Polymer(
|
| 64 |
+
min_atoms=5,
|
| 65 |
+
num_atom_types=atom_types.ATOM37_NUM,
|
| 66 |
+
atom_order=atom_types.ATOM37_ORDER,
|
| 67 |
+
),
|
| 68 |
+
mmcif_names.DNA_CHAIN: _Polymer(
|
| 69 |
+
min_atoms=21,
|
| 70 |
+
num_atom_types=atom_types.ATOM29_NUM,
|
| 71 |
+
atom_order=atom_types.ATOM29_ORDER,
|
| 72 |
+
),
|
| 73 |
+
mmcif_names.RNA_CHAIN: _Polymer(
|
| 74 |
+
min_atoms=20,
|
| 75 |
+
num_atom_types=atom_types.ATOM29_NUM,
|
| 76 |
+
atom_order=atom_types.ATOM29_ORDER,
|
| 77 |
+
),
|
| 78 |
+
}
|
| 79 |
+
|
| 80 |
+
|
| 81 |
+
def _encode_restype(
|
| 82 |
+
chain_poly_type: str,
|
| 83 |
+
sequence: str,
|
| 84 |
+
) -> Sequence[int]:
|
| 85 |
+
"""Encodes a sequence of residue names as a sequence of ints.
|
| 86 |
+
|
| 87 |
+
Args:
|
| 88 |
+
chain_poly_type: Polymer chain type to determine sequence encoding.
|
| 89 |
+
sequence: Polymer residues. Protein encoded by single letters. RNA and DNA
|
| 90 |
+
encoded by multi-letter CCD codes.
|
| 91 |
+
|
| 92 |
+
Returns:
|
| 93 |
+
A sequence of integers encoding amino acid types for the given chain type.
|
| 94 |
+
"""
|
| 95 |
+
if chain_poly_type == mmcif_names.PROTEIN_CHAIN:
|
| 96 |
+
return [
|
| 97 |
+
residue_names.PROTEIN_TYPES_ONE_LETTER_WITH_UNKNOWN_AND_GAP_TO_INT[
|
| 98 |
+
_STANDARDIZED_AA.get(res, res)
|
| 99 |
+
]
|
| 100 |
+
for res in sequence
|
| 101 |
+
]
|
| 102 |
+
|
| 103 |
+
unk_nucleic = residue_names.UNK_NUCLEIC_ONE_LETTER
|
| 104 |
+
unk_nucleic_idx = residue_names.POLYMER_TYPES_ORDER_WITH_UNKNOWN_AND_GAP[
|
| 105 |
+
unk_nucleic
|
| 106 |
+
]
|
| 107 |
+
if chain_poly_type == mmcif_names.RNA_CHAIN:
|
| 108 |
+
return [
|
| 109 |
+
residue_names.POLYMER_TYPES_ORDER_WITH_UNKNOWN_AND_GAP.get(
|
| 110 |
+
res, unk_nucleic_idx
|
| 111 |
+
)
|
| 112 |
+
for res in sequence
|
| 113 |
+
]
|
| 114 |
+
elif chain_poly_type == mmcif_names.DNA_CHAIN:
|
| 115 |
+
# Map UNK DNA to the generic nucleic UNK (N), which happens to also be the
|
| 116 |
+
# same as the RNA UNK.
|
| 117 |
+
return [
|
| 118 |
+
residue_names.POLYMER_TYPES_ORDER_WITH_UNKNOWN_AND_GAP.get(
|
| 119 |
+
residue_names.DNA_COMMON_ONE_TO_TWO.get(res, unk_nucleic),
|
| 120 |
+
unk_nucleic_idx,
|
| 121 |
+
)
|
| 122 |
+
for res in sequence
|
| 123 |
+
]
|
| 124 |
+
|
| 125 |
+
raise NotImplementedError(f'"{chain_poly_type}" unsupported.')
|
| 126 |
+
|
| 127 |
+
|
| 128 |
+
_DAYS_BEFORE_QUERY_DATE: Final[int] = 60
|
| 129 |
+
_HIT_DESCRIPTION_REGEX = re.compile(
|
| 130 |
+
r'(?P<pdb_id>[a-z0-9]{4,})_(?P<chain_id>\w+)/(?P<start>\d+)-(?P<end>\d+) '
|
| 131 |
+
r'.* length:(?P<length>\d+)\b.*'
|
| 132 |
+
)
|
| 133 |
+
|
| 134 |
+
_STANDARDIZED_AA = {'B': 'D', 'J': 'X', 'O': 'X', 'U': 'C', 'Z': 'E'}
|
| 135 |
+
|
| 136 |
+
|
| 137 |
+
class Error(Exception):
|
| 138 |
+
"""Base class for exceptions."""
|
| 139 |
+
|
| 140 |
+
|
| 141 |
+
class HitDateError(Error):
|
| 142 |
+
"""An error indicating that invalid release date was detected."""
|
| 143 |
+
|
| 144 |
+
|
| 145 |
+
class InvalidTemplateError(Error):
|
| 146 |
+
"""An error indicating that template is invalid."""
|
| 147 |
+
|
| 148 |
+
|
| 149 |
+
@dataclasses.dataclass(frozen=True, kw_only=True)
|
| 150 |
+
class Hit:
|
| 151 |
+
"""Template hit metrics derived from the MSA for filtering and featurising.
|
| 152 |
+
|
| 153 |
+
Attributes:
|
| 154 |
+
pdb_id: The PDB ID of the hit.
|
| 155 |
+
auth_chain_id: The author chain ID of the hit.
|
| 156 |
+
hmmsearch_sequence: Hit sequence as given in hmmsearch a3m output.
|
| 157 |
+
structure_sequence: Hit sequence as given in PDB structure.
|
| 158 |
+
unresolved_res_indices: Indices of unresolved residues in the structure
|
| 159 |
+
sequence. 0-based.
|
| 160 |
+
query_sequence: The query nucleotide/amino acid sequence.
|
| 161 |
+
start_index: The start index of the sequence relative to the full PDB seqres
|
| 162 |
+
sequence. Inclusive and uses 0-based indexing.
|
| 163 |
+
end_index: The end index of the sequence relative to the full PDB seqres
|
| 164 |
+
sequence. Exclusive and uses 0-based indexing.
|
| 165 |
+
full_length: Length of the full PDB seqres sequence. This can be different
|
| 166 |
+
from the length from the actual sequence we get from the mmCIF and we use
|
| 167 |
+
this to detect whether we need to realign or not.
|
| 168 |
+
release_date: The release date of the PDB corresponding to this hit.
|
| 169 |
+
chain_poly_type: The polymer type of the selected hit structure.
|
| 170 |
+
"""
|
| 171 |
+
|
| 172 |
+
pdb_id: str
|
| 173 |
+
auth_chain_id: str
|
| 174 |
+
hmmsearch_sequence: str
|
| 175 |
+
structure_sequence: str
|
| 176 |
+
unresolved_res_indices: Sequence[int] | None
|
| 177 |
+
query_sequence: str
|
| 178 |
+
start_index: int
|
| 179 |
+
end_index: int
|
| 180 |
+
full_length: int
|
| 181 |
+
release_date: datetime.date
|
| 182 |
+
chain_poly_type: str
|
| 183 |
+
|
| 184 |
+
@functools.cached_property
|
| 185 |
+
def query_to_hit_mapping(self) -> Mapping[int, int]:
|
| 186 |
+
"""0-based query index to hit index mapping."""
|
| 187 |
+
query_to_hit_mapping = {}
|
| 188 |
+
hit_index = 0
|
| 189 |
+
query_index = 0
|
| 190 |
+
for residue in self.hmmsearch_sequence:
|
| 191 |
+
# Gap inserted in the template
|
| 192 |
+
if residue == '-':
|
| 193 |
+
query_index += 1
|
| 194 |
+
# Deleted residue in the template (would be a gap in the query).
|
| 195 |
+
elif residue.islower():
|
| 196 |
+
hit_index += 1
|
| 197 |
+
# Normal aligned residue, in both query and template. Add to mapping.
|
| 198 |
+
elif residue.isupper():
|
| 199 |
+
query_to_hit_mapping[query_index] = hit_index
|
| 200 |
+
query_index += 1
|
| 201 |
+
hit_index += 1
|
| 202 |
+
|
| 203 |
+
structure_subseq = self.structure_sequence[
|
| 204 |
+
self.start_index : self.end_index
|
| 205 |
+
]
|
| 206 |
+
if self.matching_sequence != structure_subseq:
|
| 207 |
+
# The seqres sequence doesn't match the structure sequence. Two cases:
|
| 208 |
+
# 1. The sequences have the same length. The sequences are different
|
| 209 |
+
# because our 3->1 residue code mapping is different from the one PDB
|
| 210 |
+
# uses. We don't do anything in this case as both sequences have the
|
| 211 |
+
# same length, so the original query to hit mapping stays valid.
|
| 212 |
+
# 2. The sequences don't have the same length, the one in structure is
|
| 213 |
+
# shorter. In this case we change the mapping to match the actual
|
| 214 |
+
# structure sequence using a simple realignment algorithm.
|
| 215 |
+
# This procedure was validated on all PDB seqres (2023_01_12) sequences
|
| 216 |
+
# and handles all cases that can happen.
|
| 217 |
+
if self.full_length != len(self.structure_sequence):
|
| 218 |
+
return template_realign.realign_hit_to_structure(
|
| 219 |
+
hit_sequence=self.matching_sequence,
|
| 220 |
+
hit_start_index=self.start_index,
|
| 221 |
+
hit_end_index=self.end_index,
|
| 222 |
+
full_length=self.full_length,
|
| 223 |
+
structure_sequence=self.structure_sequence,
|
| 224 |
+
query_to_hit_mapping=query_to_hit_mapping,
|
| 225 |
+
)
|
| 226 |
+
|
| 227 |
+
# Hmmsearch returns a subsequence and so far indices have been relative to
|
| 228 |
+
# the subsequence. Add an offset to index relative to the full structure
|
| 229 |
+
# sequence.
|
| 230 |
+
return {q: h + self.start_index for q, h in query_to_hit_mapping.items()}
|
| 231 |
+
|
| 232 |
+
@property
|
| 233 |
+
def matching_sequence(self) -> str:
|
| 234 |
+
"""Returns the matching hit sequence including insertions.
|
| 235 |
+
|
| 236 |
+
Make deleted residues uppercase and remove gaps ("-").
|
| 237 |
+
"""
|
| 238 |
+
return self.hmmsearch_sequence.upper().replace('-', '')
|
| 239 |
+
|
| 240 |
+
@functools.cached_property
|
| 241 |
+
def output_templates_sequence(self) -> str:
|
| 242 |
+
"""Returns the final template sequence."""
|
| 243 |
+
result_seq = ['-'] * len(self.query_sequence)
|
| 244 |
+
for query_index, template_index in self.query_to_hit_mapping.items():
|
| 245 |
+
result_seq[query_index] = self.structure_sequence[template_index]
|
| 246 |
+
return ''.join(result_seq)
|
| 247 |
+
|
| 248 |
+
@property
|
| 249 |
+
def length_ratio(self) -> float:
|
| 250 |
+
"""Ratio of the length of the hit sequence to the query."""
|
| 251 |
+
return len(self.matching_sequence) / len(self.query_sequence)
|
| 252 |
+
|
| 253 |
+
@property
|
| 254 |
+
def align_ratio(self) -> float:
|
| 255 |
+
"""Ratio of the number of aligned residues to the query length."""
|
| 256 |
+
return len(self.query_to_hit_mapping) / len(self.query_sequence)
|
| 257 |
+
|
| 258 |
+
@functools.cached_property
|
| 259 |
+
def is_valid(self) -> bool:
|
| 260 |
+
"""Whether hit can be used as a template."""
|
| 261 |
+
if self.unresolved_res_indices is None:
|
| 262 |
+
return False
|
| 263 |
+
|
| 264 |
+
return bool(
|
| 265 |
+
set(self.query_to_hit_mapping.values())
|
| 266 |
+
- set(self.unresolved_res_indices)
|
| 267 |
+
)
|
| 268 |
+
|
| 269 |
+
@property
|
| 270 |
+
def full_name(self) -> str:
|
| 271 |
+
"""A full name of the hit."""
|
| 272 |
+
return f'{self.pdb_id}_{self.auth_chain_id}'
|
| 273 |
+
|
| 274 |
+
def __post_init__(self):
|
| 275 |
+
if not self.pdb_id.islower() and not self.pdb_id.isdigit():
|
| 276 |
+
raise ValueError(f'pdb_id must be lowercase {self.pdb_id}')
|
| 277 |
+
|
| 278 |
+
if not (0 <= self.start_index <= self.end_index):
|
| 279 |
+
raise ValueError(
|
| 280 |
+
'Start must be non-negative and less than or equal to end index. '
|
| 281 |
+
f'Range: {self.start_index}-{self.end_index}'
|
| 282 |
+
)
|
| 283 |
+
|
| 284 |
+
if len(self.matching_sequence) != (self.end_index - self.start_index):
|
| 285 |
+
raise ValueError(
|
| 286 |
+
'Sequence length must be equal to end_index - start_index. '
|
| 287 |
+
f'{len(self.matching_sequence)} != {self.end_index} - '
|
| 288 |
+
f'{self.start_index}'
|
| 289 |
+
)
|
| 290 |
+
|
| 291 |
+
if self.full_length < 0:
|
| 292 |
+
raise ValueError(f'Full length must be non-negative: {self.full_length}')
|
| 293 |
+
|
| 294 |
+
def keep(
|
| 295 |
+
self,
|
| 296 |
+
*,
|
| 297 |
+
release_date_cutoff: datetime.date | None,
|
| 298 |
+
max_subsequence_ratio: float | None,
|
| 299 |
+
min_hit_length: int | None,
|
| 300 |
+
min_align_ratio: float | None,
|
| 301 |
+
) -> bool:
|
| 302 |
+
"""Returns whether the hit should be kept.
|
| 303 |
+
|
| 304 |
+
In addition to filtering on all of the provided parameters, this method also
|
| 305 |
+
excludes hits with unresolved residues.
|
| 306 |
+
|
| 307 |
+
Args:
|
| 308 |
+
release_date_cutoff: Maximum release date of the template.
|
| 309 |
+
max_subsequence_ratio: If set, excludes hits which are an exact
|
| 310 |
+
subsequence of the query sequence, and longer than this ratio. Useful to
|
| 311 |
+
avoid ground truth leakage.
|
| 312 |
+
min_hit_length: If set, excludes hits which have fewer residues than this.
|
| 313 |
+
min_align_ratio: If set, excludes hits where the number of residues
|
| 314 |
+
aligned to the query is less than this proportion of the template
|
| 315 |
+
length.
|
| 316 |
+
"""
|
| 317 |
+
# Exclude hits which are too recent.
|
| 318 |
+
if (
|
| 319 |
+
release_date_cutoff is not None
|
| 320 |
+
and self.release_date > release_date_cutoff
|
| 321 |
+
):
|
| 322 |
+
return False
|
| 323 |
+
|
| 324 |
+
# Exclude hits which are large duplicates of the query_sequence.
|
| 325 |
+
if (
|
| 326 |
+
max_subsequence_ratio is not None
|
| 327 |
+
and self.length_ratio > max_subsequence_ratio
|
| 328 |
+
):
|
| 329 |
+
if self.matching_sequence in self.query_sequence:
|
| 330 |
+
return False
|
| 331 |
+
|
| 332 |
+
# Exclude hits which are too short.
|
| 333 |
+
if (
|
| 334 |
+
min_hit_length is not None
|
| 335 |
+
and len(self.matching_sequence) < min_hit_length
|
| 336 |
+
):
|
| 337 |
+
return False
|
| 338 |
+
|
| 339 |
+
# Exclude hits with unresolved residues.
|
| 340 |
+
if not self.is_valid:
|
| 341 |
+
return False
|
| 342 |
+
|
| 343 |
+
# Exclude hits with too few alignments.
|
| 344 |
+
try:
|
| 345 |
+
if min_align_ratio is not None and self.align_ratio <= min_align_ratio:
|
| 346 |
+
return False
|
| 347 |
+
except template_realign.AlignmentError as e:
|
| 348 |
+
logging.warning('Failed to align %s: %s', self, str(e))
|
| 349 |
+
return False
|
| 350 |
+
|
| 351 |
+
return True
|
| 352 |
+
|
| 353 |
+
|
| 354 |
+
def _filter_hits(
|
| 355 |
+
hits: Iterable[Hit],
|
| 356 |
+
release_date_cutoff: datetime.date,
|
| 357 |
+
max_subsequence_ratio: float | None,
|
| 358 |
+
min_align_ratio: float | None,
|
| 359 |
+
min_hit_length: int | None,
|
| 360 |
+
deduplicate_sequences: bool,
|
| 361 |
+
max_hits: int | None,
|
| 362 |
+
) -> Sequence[Hit]:
|
| 363 |
+
"""Filters hits based on the filter config."""
|
| 364 |
+
filtered_hits = []
|
| 365 |
+
seen_before = set()
|
| 366 |
+
for hit in hits:
|
| 367 |
+
if not hit.keep(
|
| 368 |
+
max_subsequence_ratio=max_subsequence_ratio,
|
| 369 |
+
min_align_ratio=min_align_ratio,
|
| 370 |
+
min_hit_length=min_hit_length,
|
| 371 |
+
release_date_cutoff=release_date_cutoff,
|
| 372 |
+
):
|
| 373 |
+
continue
|
| 374 |
+
|
| 375 |
+
# Remove duplicate templates, keeping the first.
|
| 376 |
+
if deduplicate_sequences:
|
| 377 |
+
if hit.output_templates_sequence in seen_before:
|
| 378 |
+
continue
|
| 379 |
+
seen_before.add(hit.output_templates_sequence)
|
| 380 |
+
|
| 381 |
+
filtered_hits.append(hit)
|
| 382 |
+
if max_hits and len(filtered_hits) == max_hits:
|
| 383 |
+
break
|
| 384 |
+
|
| 385 |
+
return filtered_hits
|
| 386 |
+
|
| 387 |
+
|
| 388 |
+
@dataclasses.dataclass(init=False)
|
| 389 |
+
class Templates:
|
| 390 |
+
"""A container for templates that were found for the given query sequence.
|
| 391 |
+
|
| 392 |
+
The structure_store is constructed from the config by default. Callers can
|
| 393 |
+
optionally supply a structure_store to the constructor to avoid the cost of
|
| 394 |
+
construction and metadata loading.
|
| 395 |
+
"""
|
| 396 |
+
|
| 397 |
+
def __init__(
|
| 398 |
+
self,
|
| 399 |
+
*,
|
| 400 |
+
query_sequence: str,
|
| 401 |
+
hits: Sequence[Hit],
|
| 402 |
+
max_template_date: datetime.date,
|
| 403 |
+
structure_store: structure_stores.StructureStore,
|
| 404 |
+
query_release_date: datetime.date | None = None,
|
| 405 |
+
):
|
| 406 |
+
self._query_sequence = query_sequence
|
| 407 |
+
self._hits = tuple(hits)
|
| 408 |
+
self._max_template_date = max_template_date
|
| 409 |
+
self._query_release_date = query_release_date
|
| 410 |
+
self._hit_structures = {}
|
| 411 |
+
self._structure_store = structure_store
|
| 412 |
+
|
| 413 |
+
if any(h.query_sequence != self._query_sequence for h in self.hits):
|
| 414 |
+
raise ValueError('All hits must match the query sequence.')
|
| 415 |
+
|
| 416 |
+
if self._hits:
|
| 417 |
+
chain_poly_type = self._hits[0].chain_poly_type
|
| 418 |
+
if any(h.chain_poly_type != chain_poly_type for h in self.hits):
|
| 419 |
+
raise ValueError('All hits must have the same chain_poly_type.')
|
| 420 |
+
|
| 421 |
+
@classmethod
|
| 422 |
+
def from_seq_and_a3m(
|
| 423 |
+
cls,
|
| 424 |
+
*,
|
| 425 |
+
query_sequence: str,
|
| 426 |
+
msa_a3m: str,
|
| 427 |
+
max_template_date: datetime.date,
|
| 428 |
+
database_path: os.PathLike[str] | str,
|
| 429 |
+
hmmsearch_config: msa_config.HmmsearchConfig,
|
| 430 |
+
max_a3m_query_sequences: int | None,
|
| 431 |
+
structure_store: structure_stores.StructureStore,
|
| 432 |
+
filter_config: msa_config.TemplateFilterConfig | None = None,
|
| 433 |
+
query_release_date: datetime.date | None = None,
|
| 434 |
+
chain_poly_type: str = mmcif_names.PROTEIN_CHAIN,
|
| 435 |
+
) -> Self:
|
| 436 |
+
"""Creates templates from a run of hmmsearch tool against a custom a3m.
|
| 437 |
+
|
| 438 |
+
Args:
|
| 439 |
+
query_sequence: The polymer sequence of the target query.
|
| 440 |
+
msa_a3m: An a3m of related polymers aligned to the query sequence, this is
|
| 441 |
+
used to create an HMM for the hmmsearch run.
|
| 442 |
+
max_template_date: This is used to filter templates for training, ensuring
|
| 443 |
+
that they do not leak ground truth information used in testing sets.
|
| 444 |
+
database_path: A path to the sequence database to search for templates.
|
| 445 |
+
hmmsearch_config: Config with Hmmsearch settings.
|
| 446 |
+
max_a3m_query_sequences: The maximum number of input MSA sequences to use
|
| 447 |
+
to construct the profile which is then used to search for templates.
|
| 448 |
+
structure_store: Structure store to fetch template structures from.
|
| 449 |
+
filter_config: Optional config that controls which and how many hits to
|
| 450 |
+
keep. More performant than constructing and then filtering. If not
|
| 451 |
+
provided, no filtering is done.
|
| 452 |
+
query_release_date: The release_date of the template query, this is used
|
| 453 |
+
to filter templates for training, ensuring that they do not leak
|
| 454 |
+
structure information from the future.
|
| 455 |
+
chain_poly_type: The polymer type of the templates.
|
| 456 |
+
|
| 457 |
+
Returns:
|
| 458 |
+
Templates object containing a list of Hits initialised from the
|
| 459 |
+
structure_store metadata and a3m alignments.
|
| 460 |
+
"""
|
| 461 |
+
hmmsearch_a3m = run_hmmsearch_with_a3m(
|
| 462 |
+
database_path=database_path,
|
| 463 |
+
hmmsearch_config=hmmsearch_config,
|
| 464 |
+
max_a3m_query_sequences=max_a3m_query_sequences,
|
| 465 |
+
a3m=msa_a3m,
|
| 466 |
+
)
|
| 467 |
+
return cls.from_hmmsearch_a3m(
|
| 468 |
+
query_sequence=query_sequence,
|
| 469 |
+
a3m=hmmsearch_a3m,
|
| 470 |
+
max_template_date=max_template_date,
|
| 471 |
+
query_release_date=query_release_date,
|
| 472 |
+
chain_poly_type=chain_poly_type,
|
| 473 |
+
structure_store=structure_store,
|
| 474 |
+
filter_config=filter_config,
|
| 475 |
+
)
|
| 476 |
+
|
| 477 |
+
@classmethod
|
| 478 |
+
def from_hmmsearch_a3m(
|
| 479 |
+
cls,
|
| 480 |
+
*,
|
| 481 |
+
query_sequence: str,
|
| 482 |
+
a3m: str,
|
| 483 |
+
max_template_date: datetime.date,
|
| 484 |
+
structure_store: structure_stores.StructureStore,
|
| 485 |
+
filter_config: msa_config.TemplateFilterConfig | None = None,
|
| 486 |
+
query_release_date: datetime.date | None = None,
|
| 487 |
+
chain_poly_type: str = mmcif_names.PROTEIN_CHAIN,
|
| 488 |
+
) -> Self:
|
| 489 |
+
"""Creates Templates from a Hmmsearch A3M.
|
| 490 |
+
|
| 491 |
+
Args:
|
| 492 |
+
query_sequence: The polymer sequence of the target query.
|
| 493 |
+
a3m: Results of Hmmsearch in A3M format. This provides a list of potential
|
| 494 |
+
template alignments and pdb codes.
|
| 495 |
+
max_template_date: This is used to filter templates for training, ensuring
|
| 496 |
+
that they do not leak ground truth information used in testing sets.
|
| 497 |
+
structure_store: Structure store to fetch template structures from.
|
| 498 |
+
filter_config: Optional config that controls which and how many hits to
|
| 499 |
+
keep. More performant than constructing and then filtering. If not
|
| 500 |
+
provided, no filtering is done.
|
| 501 |
+
query_release_date: The release_date of the template query, this is used
|
| 502 |
+
to filter templates for training, ensuring that they do not leak
|
| 503 |
+
structure information from the future.
|
| 504 |
+
chain_poly_type: The polymer type of the templates.
|
| 505 |
+
|
| 506 |
+
Returns:
|
| 507 |
+
Templates object containing a list of Hits initialised from the
|
| 508 |
+
structure_store metadata and a3m alignments.
|
| 509 |
+
"""
|
| 510 |
+
|
| 511 |
+
def hit_generator(a3m: str):
|
| 512 |
+
if not a3m:
|
| 513 |
+
return # Hmmsearch could return an empty string if there are no hits.
|
| 514 |
+
|
| 515 |
+
for hit_seq, hit_desc in parsers.lazy_parse_fasta_string(a3m):
|
| 516 |
+
pdb_id, auth_chain_id, start, end, full_length = _parse_hit_description(
|
| 517 |
+
hit_desc
|
| 518 |
+
)
|
| 519 |
+
|
| 520 |
+
release_date, sequence, unresolved_res_ids = _parse_hit_metadata(
|
| 521 |
+
structure_store, pdb_id, auth_chain_id
|
| 522 |
+
)
|
| 523 |
+
if unresolved_res_ids is None:
|
| 524 |
+
continue
|
| 525 |
+
|
| 526 |
+
# seq_unresolved_res_num are 1-based, setting to 0-based indices.
|
| 527 |
+
unresolved_indices = [i - 1 for i in unresolved_res_ids]
|
| 528 |
+
|
| 529 |
+
yield Hit(
|
| 530 |
+
pdb_id=pdb_id,
|
| 531 |
+
auth_chain_id=auth_chain_id,
|
| 532 |
+
hmmsearch_sequence=hit_seq,
|
| 533 |
+
structure_sequence=sequence,
|
| 534 |
+
query_sequence=query_sequence,
|
| 535 |
+
unresolved_res_indices=unresolved_indices,
|
| 536 |
+
start_index=start - 1, # Raw value is residue number, not index.
|
| 537 |
+
end_index=end,
|
| 538 |
+
full_length=full_length,
|
| 539 |
+
release_date=datetime.date.fromisoformat(release_date),
|
| 540 |
+
chain_poly_type=chain_poly_type,
|
| 541 |
+
)
|
| 542 |
+
|
| 543 |
+
if filter_config is None:
|
| 544 |
+
hits = tuple(hit_generator(a3m))
|
| 545 |
+
else:
|
| 546 |
+
hits = _filter_hits(
|
| 547 |
+
hit_generator(a3m),
|
| 548 |
+
release_date_cutoff=filter_config.max_template_date,
|
| 549 |
+
max_subsequence_ratio=filter_config.max_subsequence_ratio,
|
| 550 |
+
min_align_ratio=filter_config.min_align_ratio,
|
| 551 |
+
min_hit_length=filter_config.min_hit_length,
|
| 552 |
+
deduplicate_sequences=filter_config.deduplicate_sequences,
|
| 553 |
+
max_hits=filter_config.max_hits,
|
| 554 |
+
)
|
| 555 |
+
|
| 556 |
+
return Templates(
|
| 557 |
+
query_sequence=query_sequence,
|
| 558 |
+
query_release_date=query_release_date,
|
| 559 |
+
hits=hits,
|
| 560 |
+
max_template_date=max_template_date,
|
| 561 |
+
structure_store=structure_store,
|
| 562 |
+
)
|
| 563 |
+
|
| 564 |
+
@property
|
| 565 |
+
def query_sequence(self) -> str:
|
| 566 |
+
return self._query_sequence
|
| 567 |
+
|
| 568 |
+
@property
|
| 569 |
+
def hits(self) -> tuple[Hit, ...]:
|
| 570 |
+
return self._hits
|
| 571 |
+
|
| 572 |
+
@property
|
| 573 |
+
def query_release_date(self) -> datetime.date | None:
|
| 574 |
+
return self._query_release_date
|
| 575 |
+
|
| 576 |
+
@property
|
| 577 |
+
def num_hits(self) -> int:
|
| 578 |
+
return len(self._hits)
|
| 579 |
+
|
| 580 |
+
@functools.cached_property
|
| 581 |
+
def release_date_cutoff(self) -> datetime.date:
|
| 582 |
+
if self.query_release_date is None:
|
| 583 |
+
return self._max_template_date
|
| 584 |
+
return min(
|
| 585 |
+
self._max_template_date,
|
| 586 |
+
self.query_release_date
|
| 587 |
+
- datetime.timedelta(days=_DAYS_BEFORE_QUERY_DATE),
|
| 588 |
+
)
|
| 589 |
+
|
| 590 |
+
def __repr__(self) -> str:
|
| 591 |
+
return f'Templates({self.num_hits} hits)'
|
| 592 |
+
|
| 593 |
+
def filter(
|
| 594 |
+
self,
|
| 595 |
+
*,
|
| 596 |
+
max_subsequence_ratio: float | None,
|
| 597 |
+
min_align_ratio: float | None,
|
| 598 |
+
min_hit_length: int | None,
|
| 599 |
+
deduplicate_sequences: bool,
|
| 600 |
+
max_hits: int | None,
|
| 601 |
+
) -> Self:
|
| 602 |
+
"""Returns a new Templates object with only the hits that pass all filters.
|
| 603 |
+
|
| 604 |
+
This also filters on query_release_date and max_template_date.
|
| 605 |
+
|
| 606 |
+
Args:
|
| 607 |
+
max_subsequence_ratio: If set, excludes hits which are an exact
|
| 608 |
+
subsequence of the query sequence, and longer than this ratio. Useful to
|
| 609 |
+
avoid ground truth leakage.
|
| 610 |
+
min_align_ratio: If set, excludes hits where the number of residues
|
| 611 |
+
aligned to the query is less than this proportion of the template
|
| 612 |
+
length.
|
| 613 |
+
min_hit_length: If set, excludes hits which have fewer residues than this.
|
| 614 |
+
deduplicate_sequences: Whether to exclude duplicate template sequences,
|
| 615 |
+
keeping only the first. This can be useful in increasing the diversity
|
| 616 |
+
of hits especially in the case of homomer hits.
|
| 617 |
+
max_hits: If set, excludes any hits which exceed this count.
|
| 618 |
+
"""
|
| 619 |
+
filtered_hits = _filter_hits(
|
| 620 |
+
hits=self._hits,
|
| 621 |
+
release_date_cutoff=self.release_date_cutoff,
|
| 622 |
+
max_subsequence_ratio=max_subsequence_ratio,
|
| 623 |
+
min_align_ratio=min_align_ratio,
|
| 624 |
+
min_hit_length=min_hit_length,
|
| 625 |
+
deduplicate_sequences=deduplicate_sequences,
|
| 626 |
+
max_hits=max_hits,
|
| 627 |
+
)
|
| 628 |
+
return Templates(
|
| 629 |
+
query_sequence=self.query_sequence,
|
| 630 |
+
query_release_date=self.query_release_date,
|
| 631 |
+
hits=filtered_hits,
|
| 632 |
+
max_template_date=self._max_template_date,
|
| 633 |
+
structure_store=self._structure_store,
|
| 634 |
+
)
|
| 635 |
+
|
| 636 |
+
def get_hits_with_structures(
|
| 637 |
+
self,
|
| 638 |
+
) -> Sequence[tuple[Hit, structure.Structure]]:
|
| 639 |
+
"""Returns hits + Structures, Structures filtered to the hit's chain."""
|
| 640 |
+
results = []
|
| 641 |
+
structures = {struc.name.lower(): struc for struc in self.structures}
|
| 642 |
+
for hit in self.hits:
|
| 643 |
+
if not hit.is_valid:
|
| 644 |
+
raise InvalidTemplateError(
|
| 645 |
+
'Hits must be filtered before calling get_hits_with_structures.'
|
| 646 |
+
)
|
| 647 |
+
struc = structures[hit.pdb_id]
|
| 648 |
+
label_chain_id = struc.polymer_auth_asym_id_to_label_asym_id().get(
|
| 649 |
+
hit.auth_chain_id
|
| 650 |
+
)
|
| 651 |
+
results.append((hit, struc.filter(chain_id=label_chain_id)))
|
| 652 |
+
return results
|
| 653 |
+
|
| 654 |
+
def featurize(
|
| 655 |
+
self,
|
| 656 |
+
include_ligand_features: bool = True,
|
| 657 |
+
) -> TemplateFeatures:
|
| 658 |
+
"""Featurises the templates and returns a map of feature names to features.
|
| 659 |
+
|
| 660 |
+
NB: If you don't do any prefiltering, this method might be slow to run
|
| 661 |
+
as it has to fetch many CIFs and featurize them all.
|
| 662 |
+
|
| 663 |
+
Args:
|
| 664 |
+
include_ligand_features: Whether to compute ligand features.
|
| 665 |
+
|
| 666 |
+
Returns:
|
| 667 |
+
Template features: A mapping of template feature labels to features, which
|
| 668 |
+
may be numpy arrays, bytes objects, or for the special case of label
|
| 669 |
+
`ligand_features` (if `include_ligand_features` is True), a nested
|
| 670 |
+
feature map of labels to numpy arrays.
|
| 671 |
+
|
| 672 |
+
Raises:
|
| 673 |
+
InvalidTemplateError: If hits haven't been filtered before featurization.
|
| 674 |
+
"""
|
| 675 |
+
hits_by_pdb_id = {}
|
| 676 |
+
for idx, hit in enumerate(self.hits):
|
| 677 |
+
if not hit.is_valid:
|
| 678 |
+
raise InvalidTemplateError(
|
| 679 |
+
f'Hits must be filtered before featurizing, got unprocessed {hit=}'
|
| 680 |
+
)
|
| 681 |
+
hits_by_pdb_id.setdefault(hit.pdb_id, []).append((idx, hit))
|
| 682 |
+
|
| 683 |
+
unsorted_features = []
|
| 684 |
+
for struc in self.structures:
|
| 685 |
+
pdb_id = str(struc.name).lower()
|
| 686 |
+
for idx, hit in hits_by_pdb_id[pdb_id]:
|
| 687 |
+
try:
|
| 688 |
+
label_chain_id = struc.polymer_auth_asym_id_to_label_asym_id()[
|
| 689 |
+
hit.auth_chain_id
|
| 690 |
+
]
|
| 691 |
+
hit_features = {
|
| 692 |
+
**get_polymer_features(
|
| 693 |
+
chain=struc.filter(chain_id=label_chain_id),
|
| 694 |
+
chain_poly_type=hit.chain_poly_type,
|
| 695 |
+
query_sequence_length=len(hit.query_sequence),
|
| 696 |
+
query_to_hit_mapping=hit.query_to_hit_mapping,
|
| 697 |
+
),
|
| 698 |
+
}
|
| 699 |
+
if include_ligand_features:
|
| 700 |
+
hit_features['ligand_features'] = _get_ligand_features(struc)
|
| 701 |
+
unsorted_features.append((idx, hit_features))
|
| 702 |
+
except Error as e:
|
| 703 |
+
raise type(e)(f'Failed to featurise {hit=}') from e
|
| 704 |
+
|
| 705 |
+
sorted_features = sorted(unsorted_features, key=lambda x: x[0])
|
| 706 |
+
sorted_features = [feat for _, feat in sorted_features]
|
| 707 |
+
return package_template_features(
|
| 708 |
+
hit_features=sorted_features,
|
| 709 |
+
include_ligand_features=include_ligand_features,
|
| 710 |
+
)
|
| 711 |
+
|
| 712 |
+
@property
|
| 713 |
+
def structures(self) -> Iterator[structure.Structure]:
|
| 714 |
+
"""Yields template structures for each unique PDB ID among hits.
|
| 715 |
+
|
| 716 |
+
If there are multiple hits in the same Structure, the Structure will be
|
| 717 |
+
included only once by this method.
|
| 718 |
+
|
| 719 |
+
Yields:
|
| 720 |
+
A Structure object for each unique PDB ID among hits.
|
| 721 |
+
|
| 722 |
+
Raises:
|
| 723 |
+
HitDateError: If template's release date exceeds max cutoff date.
|
| 724 |
+
"""
|
| 725 |
+
|
| 726 |
+
for hit in self.hits:
|
| 727 |
+
if hit.release_date > self.release_date_cutoff: # pylint: disable=comparison-with-callable
|
| 728 |
+
raise HitDateError(
|
| 729 |
+
f'Invalid release date for hit {hit.pdb_id=}, when release date '
|
| 730 |
+
f'cutoff is {self.release_date_cutoff}.'
|
| 731 |
+
)
|
| 732 |
+
|
| 733 |
+
# Get the set of pdbs to load. In particular, remove duplicate PDB IDs.
|
| 734 |
+
targets_to_load = tuple({hit.pdb_id for hit in self.hits})
|
| 735 |
+
|
| 736 |
+
for target_name in targets_to_load:
|
| 737 |
+
yield structure.from_mmcif(
|
| 738 |
+
mmcif_string=self._structure_store.get_mmcif_str(target_name),
|
| 739 |
+
fix_mse_residues=True,
|
| 740 |
+
fix_arginines=True,
|
| 741 |
+
include_water=False,
|
| 742 |
+
include_bonds=False,
|
| 743 |
+
include_other=True, # For non-standard polymer chains.
|
| 744 |
+
)
|
| 745 |
+
|
| 746 |
+
|
| 747 |
+
def _parse_hit_description(description: str) -> tuple[str, str, int, int, int]:
|
| 748 |
+
"""Parses the hmmsearch A3M sequence description line."""
|
| 749 |
+
# Example lines (protein, nucleic, no description):
|
| 750 |
+
# >4pqx_A/2-217 [subseq from] mol:protein length:217 Free text
|
| 751 |
+
# >4pqx_A/2-217 [subseq from] mol:na length:217 Free text
|
| 752 |
+
# >5g3r_A/1-55 [subseq from] mol:protein length:352
|
| 753 |
+
if match := re.fullmatch(_HIT_DESCRIPTION_REGEX, description):
|
| 754 |
+
return (
|
| 755 |
+
match['pdb_id'],
|
| 756 |
+
match['chain_id'],
|
| 757 |
+
int(match['start']),
|
| 758 |
+
int(match['end']),
|
| 759 |
+
int(match['length']),
|
| 760 |
+
)
|
| 761 |
+
else:
|
| 762 |
+
raise ValueError(f'Could not parse description "{description}"')
|
| 763 |
+
|
| 764 |
+
|
| 765 |
+
def _parse_hit_metadata(
|
| 766 |
+
structure_store: structure_stores.StructureStore,
|
| 767 |
+
pdb_id: str,
|
| 768 |
+
auth_chain_id: str,
|
| 769 |
+
) -> tuple[Any, str | None, Sequence[int] | None]:
|
| 770 |
+
"""Parse hit metadata by parsing mmCIF from structure store."""
|
| 771 |
+
try:
|
| 772 |
+
cif = mmcif.from_string(structure_store.get_mmcif_str(pdb_id))
|
| 773 |
+
except structure_stores.NotFoundError:
|
| 774 |
+
logging.warning(
|
| 775 |
+
'Failed to get mmCIF for %s (author chain %s).', pdb_id, auth_chain_id
|
| 776 |
+
)
|
| 777 |
+
return None, None, None
|
| 778 |
+
release_date = mmcif.get_release_date(cif)
|
| 779 |
+
|
| 780 |
+
try:
|
| 781 |
+
struc = structure.from_parsed_mmcif(
|
| 782 |
+
cif,
|
| 783 |
+
model_id=structure.ModelID.ALL,
|
| 784 |
+
include_water=True,
|
| 785 |
+
include_other=True,
|
| 786 |
+
include_bonds=False,
|
| 787 |
+
)
|
| 788 |
+
except ValueError:
|
| 789 |
+
struc = structure.from_parsed_mmcif(
|
| 790 |
+
cif,
|
| 791 |
+
model_id=structure.ModelID.FIRST,
|
| 792 |
+
include_water=True,
|
| 793 |
+
include_other=True,
|
| 794 |
+
include_bonds=False,
|
| 795 |
+
)
|
| 796 |
+
|
| 797 |
+
sequence = struc.polymer_author_chain_single_letter_sequence(
|
| 798 |
+
include_missing_residues=True,
|
| 799 |
+
protein=True,
|
| 800 |
+
dna=True,
|
| 801 |
+
rna=True,
|
| 802 |
+
other=True,
|
| 803 |
+
)[auth_chain_id]
|
| 804 |
+
|
| 805 |
+
unresolved_res_ids = struc.filter(
|
| 806 |
+
chain_auth_asym_id=auth_chain_id
|
| 807 |
+
).unresolved_residues.id
|
| 808 |
+
|
| 809 |
+
return release_date, sequence, unresolved_res_ids
|
| 810 |
+
|
| 811 |
+
|
| 812 |
+
def get_polymer_features(
|
| 813 |
+
*,
|
| 814 |
+
chain: structure.Structure,
|
| 815 |
+
chain_poly_type: str,
|
| 816 |
+
query_sequence_length: int,
|
| 817 |
+
query_to_hit_mapping: Mapping[int, int],
|
| 818 |
+
) -> Mapping[str, Any]:
|
| 819 |
+
"""Returns features for this polymer chain.
|
| 820 |
+
|
| 821 |
+
Args:
|
| 822 |
+
chain: Structure object representing the template. Must be already filtered
|
| 823 |
+
to a single chain.
|
| 824 |
+
chain_poly_type: The chain polymer type (protein, DNA, RNA).
|
| 825 |
+
query_sequence_length: The length of the query sequence.
|
| 826 |
+
query_to_hit_mapping: 0-based query index to hit index mapping.
|
| 827 |
+
|
| 828 |
+
Returns:
|
| 829 |
+
A dictionary with polymer features for template_chain_id in the struc.
|
| 830 |
+
|
| 831 |
+
Raises:
|
| 832 |
+
ValueError: If the input structure contains more than just a single chain.
|
| 833 |
+
"""
|
| 834 |
+
# if len(chain.polymer_auth_asym_id_to_label_asym_id()) != 1:
|
| 835 |
+
# raise ValueError('The structure must be filtered to a single chain.')
|
| 836 |
+
|
| 837 |
+
if chain.name is None:
|
| 838 |
+
raise ValueError('Template structure must have a name.')
|
| 839 |
+
|
| 840 |
+
if chain.release_date is None:
|
| 841 |
+
raise ValueError(
|
| 842 |
+
f'Template structure {chain.name} must have a release date. You can do'
|
| 843 |
+
' this by setting "_pdbx_audit_revision_history.revision_date" in the'
|
| 844 |
+
' template mmCIF to a date in the ISO-8601 format (e.g. 1989-11-17).'
|
| 845 |
+
)
|
| 846 |
+
|
| 847 |
+
num_polymer_chains = len(chain.polymer_auth_asym_id_to_label_asym_id())
|
| 848 |
+
if num_polymer_chains != 1:
|
| 849 |
+
raise ValueError(
|
| 850 |
+
f'Template structure {chain.name} must be filtered to a single polymer'
|
| 851 |
+
f' chain but got a structure with {num_polymer_chains} polymer chains.'
|
| 852 |
+
)
|
| 853 |
+
|
| 854 |
+
auth_chain_id, label_chain_id = next(
|
| 855 |
+
iter(chain.polymer_auth_asym_id_to_label_asym_id().items())
|
| 856 |
+
)
|
| 857 |
+
chain_sequence = chain.chain_single_letter_sequence()[label_chain_id]
|
| 858 |
+
|
| 859 |
+
polymer = _POLYMERS[chain_poly_type]
|
| 860 |
+
res_arrays = chain.to_res_arrays(
|
| 861 |
+
include_missing_residues=True, atom_order=polymer.atom_order
|
| 862 |
+
)
|
| 863 |
+
positions = res_arrays.atom_positions
|
| 864 |
+
positions_mask = res_arrays.atom_mask
|
| 865 |
+
template_all_atom_positions = np.zeros(
|
| 866 |
+
(query_sequence_length, polymer.num_atom_types, 3), dtype=np.float64
|
| 867 |
+
)
|
| 868 |
+
template_all_atom_masks = np.zeros(
|
| 869 |
+
(query_sequence_length, polymer.num_atom_types), dtype=np.int64
|
| 870 |
+
)
|
| 871 |
+
|
| 872 |
+
template_sequence = ['-'] * query_sequence_length
|
| 873 |
+
for query_index, template_index in query_to_hit_mapping.items():
|
| 874 |
+
template_all_atom_positions[query_index] = positions[template_index]
|
| 875 |
+
template_all_atom_masks[query_index] = positions_mask[template_index]
|
| 876 |
+
template_sequence[query_index] = chain_sequence[template_index]
|
| 877 |
+
|
| 878 |
+
template_sequence = ''.join(template_sequence)
|
| 879 |
+
template_aatype = _encode_restype(chain_poly_type, template_sequence)
|
| 880 |
+
template_name = f'{chain.name.lower()}_{auth_chain_id}'
|
| 881 |
+
release_date = chain.release_date.strftime('%Y-%m-%d')
|
| 882 |
+
return {
|
| 883 |
+
'template_all_atom_positions': template_all_atom_positions,
|
| 884 |
+
'template_all_atom_masks': template_all_atom_masks,
|
| 885 |
+
'template_sequence': template_sequence.encode(),
|
| 886 |
+
'template_aatype': np.array(template_aatype, dtype=np.int32),
|
| 887 |
+
'template_domain_names': np.array(template_name.encode(), dtype=object),
|
| 888 |
+
'template_release_date': np.array(release_date.encode(), dtype=object),
|
| 889 |
+
}
|
| 890 |
+
|
| 891 |
+
|
| 892 |
+
def _get_ligand_features(
|
| 893 |
+
struc: structure.Structure,
|
| 894 |
+
) -> Mapping[str, Mapping[str, np.ndarray | bytes]]:
|
| 895 |
+
"""Returns features for the ligands in this structure."""
|
| 896 |
+
ligand_struc = struc.filter_to_entity_type(ligand=True)
|
| 897 |
+
assert ligand_struc.coords is not None
|
| 898 |
+
assert ligand_struc.atom_name is not None
|
| 899 |
+
assert ligand_struc.atom_occupancy is not None
|
| 900 |
+
|
| 901 |
+
ligand_features = {}
|
| 902 |
+
for ligand_chain_id in ligand_struc.chains:
|
| 903 |
+
idxs = np.where(ligand_struc.chain_id == ligand_chain_id)[0]
|
| 904 |
+
if idxs.shape[0]:
|
| 905 |
+
ligand_features[ligand_chain_id] = {
|
| 906 |
+
'ligand_atom_positions': (
|
| 907 |
+
ligand_struc.coords[idxs, :].astype(np.float32)
|
| 908 |
+
),
|
| 909 |
+
'ligand_atom_names': ligand_struc.atom_name[idxs].astype(object),
|
| 910 |
+
'ligand_atom_occupancies': (
|
| 911 |
+
ligand_struc.atom_occupancy[idxs].astype(np.float32)
|
| 912 |
+
),
|
| 913 |
+
'ccd_id': ligand_struc.res_name[idxs][0].encode(),
|
| 914 |
+
}
|
| 915 |
+
return ligand_features
|
| 916 |
+
|
| 917 |
+
|
| 918 |
+
def package_template_features(
|
| 919 |
+
*,
|
| 920 |
+
hit_features: Sequence[Mapping[str, Any]],
|
| 921 |
+
include_ligand_features: bool,
|
| 922 |
+
) -> Mapping[str, Any]:
|
| 923 |
+
"""Stacks polymer features, adds empty and keeps ligand features unstacked."""
|
| 924 |
+
|
| 925 |
+
features_to_include = set(_POLYMER_FEATURES)
|
| 926 |
+
if include_ligand_features:
|
| 927 |
+
features_to_include.update(_LIGAND_FEATURES)
|
| 928 |
+
|
| 929 |
+
features = {
|
| 930 |
+
feat: [single_hit_features[feat] for single_hit_features in hit_features]
|
| 931 |
+
for feat in features_to_include
|
| 932 |
+
}
|
| 933 |
+
|
| 934 |
+
stacked_features = {}
|
| 935 |
+
for k, v in features.items():
|
| 936 |
+
if k in _POLYMER_FEATURES:
|
| 937 |
+
v = np.stack(v, axis=0) if v else np.array([], dtype=_POLYMER_FEATURES[k])
|
| 938 |
+
stacked_features[k] = v
|
| 939 |
+
|
| 940 |
+
return stacked_features
|
| 941 |
+
|
| 942 |
+
|
| 943 |
+
def _resolve_path(path: os.PathLike[str] | str) -> str:
|
| 944 |
+
"""Resolves path for data dep paths, stringifies otherwise."""
|
| 945 |
+
# Data dependency paths: db baked into the binary.
|
| 946 |
+
resolved_path = resources.filename(path)
|
| 947 |
+
if os.path.exists(resolved_path):
|
| 948 |
+
return resolved_path
|
| 949 |
+
else:
|
| 950 |
+
# Other paths, e.g. local.
|
| 951 |
+
return str(path)
|
| 952 |
+
|
| 953 |
+
|
| 954 |
+
def run_hmmsearch_with_a3m(
|
| 955 |
+
*,
|
| 956 |
+
database_path: os.PathLike[str] | str,
|
| 957 |
+
hmmsearch_config: msa_config.HmmsearchConfig,
|
| 958 |
+
max_a3m_query_sequences: int | None,
|
| 959 |
+
a3m: str | None,
|
| 960 |
+
) -> str:
|
| 961 |
+
"""Runs Hmmsearch to get a3m string of hits."""
|
| 962 |
+
searcher = hmmsearch.Hmmsearch(
|
| 963 |
+
binary_path=hmmsearch_config.hmmsearch_binary_path,
|
| 964 |
+
hmmbuild_binary_path=hmmsearch_config.hmmbuild_binary_path,
|
| 965 |
+
database_path=_resolve_path(database_path),
|
| 966 |
+
e_value=hmmsearch_config.e_value,
|
| 967 |
+
inc_e=hmmsearch_config.inc_e,
|
| 968 |
+
dom_e=hmmsearch_config.dom_e,
|
| 969 |
+
incdom_e=hmmsearch_config.incdom_e,
|
| 970 |
+
alphabet=hmmsearch_config.alphabet,
|
| 971 |
+
filter_f1=hmmsearch_config.filter_f1,
|
| 972 |
+
filter_f2=hmmsearch_config.filter_f2,
|
| 973 |
+
filter_f3=hmmsearch_config.filter_f3,
|
| 974 |
+
filter_max=hmmsearch_config.filter_max,
|
| 975 |
+
)
|
| 976 |
+
# STO enables us to annotate query non-gap columns as reference columns.
|
| 977 |
+
sto = parsers.convert_a3m_to_stockholm(a3m, max_a3m_query_sequences)
|
| 978 |
+
return searcher.query_with_sto(sto, model_construction='hand')
|
flax_model/alphafold3/data/tools/__init__.py
ADDED
|
File without changes
|
flax_model/alphafold3/data/tools/hmmbuild.py
ADDED
|
@@ -0,0 +1,136 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
|
| 2 |
+
|
| 3 |
+
"""A Python wrapper for hmmbuild - construct HMM profiles from MSA."""
|
| 4 |
+
|
| 5 |
+
import os
|
| 6 |
+
import re
|
| 7 |
+
import tempfile
|
| 8 |
+
from typing import Literal
|
| 9 |
+
|
| 10 |
+
from flax_model.alphafold3.data import parsers
|
| 11 |
+
from flax_model.alphafold3.data.tools import subprocess_utils
|
| 12 |
+
|
| 13 |
+
|
| 14 |
+
class Hmmbuild(object):
|
| 15 |
+
"""Python wrapper of the hmmbuild binary."""
|
| 16 |
+
|
| 17 |
+
def __init__(
|
| 18 |
+
self,
|
| 19 |
+
*,
|
| 20 |
+
binary_path: str,
|
| 21 |
+
singlemx: bool = False,
|
| 22 |
+
alphabet: str | None = None,
|
| 23 |
+
):
|
| 24 |
+
"""Initializes the Python hmmbuild wrapper.
|
| 25 |
+
|
| 26 |
+
Args:
|
| 27 |
+
binary_path: The path to the hmmbuild executable.
|
| 28 |
+
singlemx: Whether to use --singlemx flag. If True, it forces HMMBuild to
|
| 29 |
+
just use a common substitution score matrix.
|
| 30 |
+
alphabet: The alphabet to assert when building a profile. Useful when
|
| 31 |
+
hmmbuild cannot guess the alphabet. If None, no alphabet is asserted.
|
| 32 |
+
|
| 33 |
+
Raises:
|
| 34 |
+
RuntimeError: If hmmbuild binary not found within the path.
|
| 35 |
+
"""
|
| 36 |
+
self._binary_path = binary_path
|
| 37 |
+
self._singlemx = singlemx
|
| 38 |
+
self._alphabet = alphabet
|
| 39 |
+
|
| 40 |
+
subprocess_utils.check_binary_exists(path=self._binary_path, name='hmmbuild')
|
| 41 |
+
|
| 42 |
+
def build_profile_from_sto(self, sto: str, model_construction='fast') -> str:
|
| 43 |
+
"""Builds a HHM for the aligned sequences given as an A3M string.
|
| 44 |
+
|
| 45 |
+
Args:
|
| 46 |
+
sto: A string with the aligned sequences in the Stockholm format.
|
| 47 |
+
model_construction: Whether to use reference annotation in the msa to
|
| 48 |
+
determine consensus columns ('hand') or default ('fast').
|
| 49 |
+
|
| 50 |
+
Returns:
|
| 51 |
+
A string with the profile in the HMM format.
|
| 52 |
+
|
| 53 |
+
Raises:
|
| 54 |
+
RuntimeError: If hmmbuild fails.
|
| 55 |
+
"""
|
| 56 |
+
return self._build_profile(
|
| 57 |
+
sto, informat='stockholm', model_construction=model_construction
|
| 58 |
+
)
|
| 59 |
+
|
| 60 |
+
def build_profile_from_a3m(self, a3m: str) -> str:
|
| 61 |
+
"""Builds a HHM for the aligned sequences given as an A3M string.
|
| 62 |
+
|
| 63 |
+
Args:
|
| 64 |
+
a3m: A string with the aligned sequences in the A3M format.
|
| 65 |
+
|
| 66 |
+
Returns:
|
| 67 |
+
A string with the profile in the HMM format.
|
| 68 |
+
|
| 69 |
+
Raises:
|
| 70 |
+
RuntimeError: If hmmbuild fails.
|
| 71 |
+
"""
|
| 72 |
+
lines = []
|
| 73 |
+
for sequence, description in parsers.lazy_parse_fasta_string(a3m):
|
| 74 |
+
sequence = re.sub('[a-z]+', '', sequence) # Remove inserted residues.
|
| 75 |
+
lines.append(f'>{description}\n{sequence}\n')
|
| 76 |
+
msa = ''.join(lines)
|
| 77 |
+
return self._build_profile(msa, informat='afa')
|
| 78 |
+
|
| 79 |
+
def _build_profile(
|
| 80 |
+
self,
|
| 81 |
+
msa: str,
|
| 82 |
+
informat: Literal['afa', 'stockholm'],
|
| 83 |
+
model_construction: str = 'fast',
|
| 84 |
+
) -> str:
|
| 85 |
+
"""Builds a HMM for the aligned sequences given as an MSA string.
|
| 86 |
+
|
| 87 |
+
Args:
|
| 88 |
+
msa: A string with the aligned sequences, in A3M or STO format.
|
| 89 |
+
informat: One of 'afa' (aligned FASTA) or 'sto' (Stockholm).
|
| 90 |
+
model_construction: Whether to use reference annotation in the msa to
|
| 91 |
+
determine consensus columns ('hand') or default ('fast').
|
| 92 |
+
|
| 93 |
+
Returns:
|
| 94 |
+
A string with the profile in the HMM format.
|
| 95 |
+
|
| 96 |
+
Raises:
|
| 97 |
+
RuntimeError: If hmmbuild fails.
|
| 98 |
+
ValueError: If unspecified arguments are provided.
|
| 99 |
+
"""
|
| 100 |
+
if model_construction not in {'hand', 'fast'}:
|
| 101 |
+
raise ValueError(f'Bad {model_construction=}. Only hand or fast allowed.')
|
| 102 |
+
|
| 103 |
+
with tempfile.TemporaryDirectory() as query_tmp_dir:
|
| 104 |
+
input_msa_path = os.path.join(query_tmp_dir, 'query.msa')
|
| 105 |
+
output_hmm_path = os.path.join(query_tmp_dir, 'output.hmm')
|
| 106 |
+
|
| 107 |
+
with open(input_msa_path, 'w') as f:
|
| 108 |
+
f.write(msa)
|
| 109 |
+
|
| 110 |
+
# Specify the format as we don't specify the input file extension. See
|
| 111 |
+
# https://github.com/EddyRivasLab/hmmer/issues/321 for more details.
|
| 112 |
+
cmd_flags = ['--informat', informat]
|
| 113 |
+
# If adding flags, we have to do so before the output and input:
|
| 114 |
+
if model_construction == 'hand':
|
| 115 |
+
cmd_flags.append(f'--{model_construction}')
|
| 116 |
+
if self._singlemx:
|
| 117 |
+
cmd_flags.append('--singlemx')
|
| 118 |
+
if self._alphabet:
|
| 119 |
+
cmd_flags.append(f'--{self._alphabet}')
|
| 120 |
+
|
| 121 |
+
cmd_flags.extend([output_hmm_path, input_msa_path])
|
| 122 |
+
|
| 123 |
+
cmd = [self._binary_path, *cmd_flags]
|
| 124 |
+
|
| 125 |
+
subprocess_utils.run(
|
| 126 |
+
cmd=cmd,
|
| 127 |
+
cmd_name='Hmmbuild',
|
| 128 |
+
log_stdout=False,
|
| 129 |
+
log_stderr=True,
|
| 130 |
+
log_on_process_error=True,
|
| 131 |
+
)
|
| 132 |
+
|
| 133 |
+
with open(output_hmm_path) as f:
|
| 134 |
+
hmm = f.read()
|
| 135 |
+
|
| 136 |
+
return hmm
|
flax_model/alphafold3/data/tools/hmmsearch.py
ADDED
|
@@ -0,0 +1,141 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
|
| 2 |
+
|
| 3 |
+
"""A Python wrapper for hmmsearch - search profile against a sequence db."""
|
| 4 |
+
|
| 5 |
+
import os
|
| 6 |
+
import tempfile
|
| 7 |
+
|
| 8 |
+
from absl import logging
|
| 9 |
+
from flax_model.alphafold3.data import parsers
|
| 10 |
+
from flax_model.alphafold3.data.tools import hmmbuild
|
| 11 |
+
from flax_model.alphafold3.data.tools import subprocess_utils
|
| 12 |
+
|
| 13 |
+
|
| 14 |
+
class Hmmsearch(object):
|
| 15 |
+
"""Python wrapper of the hmmsearch binary."""
|
| 16 |
+
|
| 17 |
+
def __init__(
|
| 18 |
+
self,
|
| 19 |
+
*,
|
| 20 |
+
binary_path: str,
|
| 21 |
+
hmmbuild_binary_path: str,
|
| 22 |
+
database_path: str,
|
| 23 |
+
alphabet: str = 'amino',
|
| 24 |
+
filter_f1: float | None = None,
|
| 25 |
+
filter_f2: float | None = None,
|
| 26 |
+
filter_f3: float | None = None,
|
| 27 |
+
e_value: float | None = None,
|
| 28 |
+
inc_e: float | None = None,
|
| 29 |
+
dom_e: float | None = None,
|
| 30 |
+
incdom_e: float | None = None,
|
| 31 |
+
filter_max: bool = False,
|
| 32 |
+
):
|
| 33 |
+
"""Initializes the Python hmmsearch wrapper.
|
| 34 |
+
|
| 35 |
+
Args:
|
| 36 |
+
binary_path: The path to the hmmsearch executable.
|
| 37 |
+
hmmbuild_binary_path: The path to the hmmbuild executable. Used to build
|
| 38 |
+
an hmm from an input a3m.
|
| 39 |
+
database_path: The path to the hmmsearch database (FASTA format).
|
| 40 |
+
alphabet: Chain type e.g. amino, rna, dna.
|
| 41 |
+
filter_f1: MSV and biased composition pre-filter, set to >1.0 to turn off.
|
| 42 |
+
filter_f2: Viterbi pre-filter, set to >1.0 to turn off.
|
| 43 |
+
filter_f3: Forward pre-filter, set to >1.0 to turn off.
|
| 44 |
+
e_value: E-value criteria for inclusion in tblout.
|
| 45 |
+
inc_e: E-value criteria for inclusion in MSA/next round.
|
| 46 |
+
dom_e: Domain e-value criteria for inclusion in tblout.
|
| 47 |
+
incdom_e: Domain e-value criteria for inclusion of domains in MSA/next
|
| 48 |
+
round.
|
| 49 |
+
filter_max: Remove all filters, will ignore all filter_f* settings.
|
| 50 |
+
|
| 51 |
+
Raises:
|
| 52 |
+
RuntimeError: If hmmsearch binary not found within the path.
|
| 53 |
+
"""
|
| 54 |
+
self._binary_path = binary_path
|
| 55 |
+
self._hmmbuild_runner = hmmbuild.Hmmbuild(
|
| 56 |
+
alphabet=alphabet, binary_path=hmmbuild_binary_path
|
| 57 |
+
)
|
| 58 |
+
self._database_path = database_path
|
| 59 |
+
flags = []
|
| 60 |
+
if filter_max:
|
| 61 |
+
flags.append('--max')
|
| 62 |
+
else:
|
| 63 |
+
if filter_f1 is not None:
|
| 64 |
+
flags.extend(('--F1', filter_f1))
|
| 65 |
+
if filter_f2 is not None:
|
| 66 |
+
flags.extend(('--F2', filter_f2))
|
| 67 |
+
if filter_f3 is not None:
|
| 68 |
+
flags.extend(('--F3', filter_f3))
|
| 69 |
+
|
| 70 |
+
if e_value is not None:
|
| 71 |
+
flags.extend(('-E', e_value))
|
| 72 |
+
if inc_e is not None:
|
| 73 |
+
flags.extend(('--incE', inc_e))
|
| 74 |
+
if dom_e is not None:
|
| 75 |
+
flags.extend(('--domE', dom_e))
|
| 76 |
+
if incdom_e is not None:
|
| 77 |
+
flags.extend(('--incdomE', incdom_e))
|
| 78 |
+
|
| 79 |
+
self._flags = tuple(map(str, flags))
|
| 80 |
+
|
| 81 |
+
subprocess_utils.check_binary_exists(
|
| 82 |
+
path=self._binary_path, name='hmmsearch'
|
| 83 |
+
)
|
| 84 |
+
|
| 85 |
+
if not os.path.exists(self._database_path):
|
| 86 |
+
logging.error('Could not find hmmsearch database %s', database_path)
|
| 87 |
+
raise ValueError(f'Could not find hmmsearch database {database_path}')
|
| 88 |
+
|
| 89 |
+
def query_with_hmm(self, hmm: str) -> str:
|
| 90 |
+
"""Queries the database using hmmsearch using a given hmm."""
|
| 91 |
+
with tempfile.TemporaryDirectory() as query_tmp_dir:
|
| 92 |
+
hmm_input_path = os.path.join(query_tmp_dir, 'query.hmm')
|
| 93 |
+
sto_out_path = os.path.join(query_tmp_dir, 'output.sto')
|
| 94 |
+
with open(hmm_input_path, 'w') as f:
|
| 95 |
+
f.write(hmm)
|
| 96 |
+
|
| 97 |
+
cmd = [
|
| 98 |
+
self._binary_path,
|
| 99 |
+
'--noali', # Don't include the alignment in stdout.
|
| 100 |
+
*('--cpu', '8'),
|
| 101 |
+
]
|
| 102 |
+
# If adding flags, we have to do so before the output and input:
|
| 103 |
+
if self._flags:
|
| 104 |
+
cmd.extend(self._flags)
|
| 105 |
+
cmd.extend([
|
| 106 |
+
*('-A', sto_out_path),
|
| 107 |
+
hmm_input_path,
|
| 108 |
+
self._database_path,
|
| 109 |
+
])
|
| 110 |
+
|
| 111 |
+
subprocess_utils.run(
|
| 112 |
+
cmd=cmd,
|
| 113 |
+
cmd_name=f'Hmmsearch ({os.path.basename(self._database_path)})',
|
| 114 |
+
log_stdout=False,
|
| 115 |
+
log_stderr=True,
|
| 116 |
+
log_on_process_error=True,
|
| 117 |
+
)
|
| 118 |
+
|
| 119 |
+
with open(sto_out_path) as f:
|
| 120 |
+
a3m_out = parsers.convert_stockholm_to_a3m(
|
| 121 |
+
f, remove_first_row_gaps=False, linewidth=60
|
| 122 |
+
)
|
| 123 |
+
|
| 124 |
+
return a3m_out
|
| 125 |
+
|
| 126 |
+
def query_with_a3m(self, a3m_in: str) -> str:
|
| 127 |
+
"""Query the database using hmmsearch using a given a3m."""
|
| 128 |
+
|
| 129 |
+
# Only the "fast" model construction makes sense with A3M, as it doesn't
|
| 130 |
+
# have any way to annotate reference columns.
|
| 131 |
+
hmm = self._hmmbuild_runner.build_profile_from_a3m(a3m_in)
|
| 132 |
+
return self.query_with_hmm(hmm)
|
| 133 |
+
|
| 134 |
+
def query_with_sto(
|
| 135 |
+
self, msa_sto: str, model_construction: str = 'fast'
|
| 136 |
+
) -> str:
|
| 137 |
+
"""Queries the database using hmmsearch using a given stockholm msa."""
|
| 138 |
+
hmm = self._hmmbuild_runner.build_profile_from_sto(
|
| 139 |
+
msa_sto, model_construction=model_construction
|
| 140 |
+
)
|
| 141 |
+
return self.query_with_hmm(hmm)
|
flax_model/alphafold3/data/tools/jackhmmer.py
ADDED
|
@@ -0,0 +1,326 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
|
| 2 |
+
|
| 3 |
+
"""Library to run Jackhmmer from Python."""
|
| 4 |
+
|
| 5 |
+
from collections.abc import Iterable, Sequence
|
| 6 |
+
from concurrent import futures
|
| 7 |
+
import heapq
|
| 8 |
+
import os
|
| 9 |
+
import pathlib
|
| 10 |
+
import shutil
|
| 11 |
+
import tempfile
|
| 12 |
+
import time
|
| 13 |
+
|
| 14 |
+
from absl import logging
|
| 15 |
+
from flax_model.alphafold3.data import parsers
|
| 16 |
+
from flax_model.alphafold3.data.tools import shards
|
| 17 |
+
from flax_model.alphafold3.data.tools import msa_tool
|
| 18 |
+
from flax_model.alphafold3.data.tools import subprocess_utils
|
| 19 |
+
|
| 20 |
+
|
| 21 |
+
class Jackhmmer(msa_tool.MsaTool):
|
| 22 |
+
"""Python wrapper of the Jackhmmer binary."""
|
| 23 |
+
|
| 24 |
+
def __init__(
|
| 25 |
+
self,
|
| 26 |
+
*,
|
| 27 |
+
binary_path: str,
|
| 28 |
+
database_path: str,
|
| 29 |
+
n_cpu: int = 8,
|
| 30 |
+
n_iter: int = 3,
|
| 31 |
+
e_value: float | None = 1e-3,
|
| 32 |
+
z_value: float | int | None = None,
|
| 33 |
+
dom_e: float | None = None,
|
| 34 |
+
dom_z_value: float | int | None = None,
|
| 35 |
+
max_sequences: int = 5000,
|
| 36 |
+
filter_f1: float = 5e-4,
|
| 37 |
+
filter_f2: float = 5e-5,
|
| 38 |
+
filter_f3: float = 5e-7,
|
| 39 |
+
max_threads: int | None = None,
|
| 40 |
+
**unused_kwargs,
|
| 41 |
+
):
|
| 42 |
+
"""Initializes the Python Jackhmmer wrapper.
|
| 43 |
+
|
| 44 |
+
NOTE: The MSA obtained by running against sharded dbs won't be always
|
| 45 |
+
exactly the same as the MSA obtained by running against an unsharded db.
|
| 46 |
+
This is because of Jackhmmer deduplication logic, which won't spot duplicate
|
| 47 |
+
hits across multiple shards. Usually this means that the sharded search
|
| 48 |
+
finds more hits (likely bounded by the number of shards), but this should
|
| 49 |
+
not pose an issue given how the results are used downstream. The problem is
|
| 50 |
+
more pronounced with deep MSAs and lower in the hit list (higher e-values).
|
| 51 |
+
|
| 52 |
+
Make sure to set the Z and domZ values when searching against a sharded
|
| 53 |
+
database, otherwise the results won't match the normal unsharded search.
|
| 54 |
+
|
| 55 |
+
Args:
|
| 56 |
+
binary_path: The path to the jackhmmer executable.
|
| 57 |
+
database_path: The path to the jackhmmer database (FASTA format). Sharded
|
| 58 |
+
file specs, e.g. `<db_path>@<num_shards>`, are supported.
|
| 59 |
+
n_cpu: The number of CPUs to give Jackhmmer.
|
| 60 |
+
n_iter: The number of Jackhmmer iterations.
|
| 61 |
+
e_value: The E-value, see Jackhmmer docs for more details.
|
| 62 |
+
z_value: The Z-value representing the number of comparisons done (i.e
|
| 63 |
+
correct database size) for E-value calculation. Make sure to set this
|
| 64 |
+
when searching against a sharded database, otherwise the e-values will
|
| 65 |
+
be incorrectly scaled.
|
| 66 |
+
dom_e: Domain e-value criteria for inclusion in tblout.
|
| 67 |
+
dom_z_value: Domain z-value representing the number of comparisons done
|
| 68 |
+
(i.e correct database size) for domain E-value calculation. Make sure to
|
| 69 |
+
set this when searching against a sharded database, otherwise the domain
|
| 70 |
+
e-values will be incorrectly scaled.
|
| 71 |
+
max_sequences: Maximum number of sequences to return in the MSA.
|
| 72 |
+
filter_f1: MSV and biased composition pre-filter, set to >1.0 to turn off.
|
| 73 |
+
filter_f2: Viterbi pre-filter, set to >1.0 to turn off.
|
| 74 |
+
filter_f3: Forward pre-filter, set to >1.0 to turn off.
|
| 75 |
+
max_threads: If given, the maximum number of threads used when running
|
| 76 |
+
sharded databases.
|
| 77 |
+
|
| 78 |
+
Raises:
|
| 79 |
+
RuntimeError: If Jackhmmer binary not found within the path.
|
| 80 |
+
ValueError: If an invalid configuration is provided in the args.
|
| 81 |
+
"""
|
| 82 |
+
self._database_path = database_path
|
| 83 |
+
|
| 84 |
+
if shard_paths := shards.get_sharded_paths(self._database_path):
|
| 85 |
+
if n_iter != 1:
|
| 86 |
+
raise ValueError('For a sharded db, only n_iter=1 is supported.')
|
| 87 |
+
if z_value is None:
|
| 88 |
+
raise ValueError(
|
| 89 |
+
'The Z-value must be set when searching against a sharded database '
|
| 90 |
+
'to correctly scale e-values.'
|
| 91 |
+
)
|
| 92 |
+
if max_sequences <= 1:
|
| 93 |
+
raise ValueError(
|
| 94 |
+
'max_sequences must be greater than 1 when running in sharded '
|
| 95 |
+
'mode, because each shard would return only the query sequence.'
|
| 96 |
+
)
|
| 97 |
+
|
| 98 |
+
self._shard_paths = shard_paths
|
| 99 |
+
self._max_threads = len(self._shard_paths)
|
| 100 |
+
if max_threads is not None:
|
| 101 |
+
self._max_threads = min(max_threads, self._max_threads)
|
| 102 |
+
logging.info('Jackhmmer running with max_threads = %d', self._max_threads)
|
| 103 |
+
else:
|
| 104 |
+
self._shard_paths = None
|
| 105 |
+
self._max_threads = None
|
| 106 |
+
|
| 107 |
+
self._binary_path = binary_path
|
| 108 |
+
subprocess_utils.check_binary_exists(
|
| 109 |
+
path=self._binary_path, name='Jackhmmer'
|
| 110 |
+
)
|
| 111 |
+
|
| 112 |
+
self._n_cpu = n_cpu
|
| 113 |
+
self._n_iter = n_iter
|
| 114 |
+
self._e_value = e_value
|
| 115 |
+
self._z_value = z_value
|
| 116 |
+
self._dom_e = dom_e
|
| 117 |
+
self._dom_z_value = dom_z_value
|
| 118 |
+
self._max_sequences = max_sequences
|
| 119 |
+
self._filter_f1 = filter_f1
|
| 120 |
+
self._filter_f2 = filter_f2
|
| 121 |
+
self._filter_f3 = filter_f3
|
| 122 |
+
|
| 123 |
+
# If Jackhmmer supports the --seq_limit flag (via our patch), use it to
|
| 124 |
+
# prevent writing out redundant sequences and increasing peak memory usage.
|
| 125 |
+
# If not, the Jackhmmer will be run without the --seq_limit flag.
|
| 126 |
+
self._supports_seq_limit = subprocess_utils.jackhmmer_seq_limit_supported(
|
| 127 |
+
self._binary_path
|
| 128 |
+
)
|
| 129 |
+
|
| 130 |
+
def query(self, target_sequence: str) -> msa_tool.MsaToolResult:
|
| 131 |
+
"""Query the database (sharded or unsharded) using Jackhmmer."""
|
| 132 |
+
if self._shard_paths:
|
| 133 |
+
# Sharded case, run the query against each database shard in parallel.
|
| 134 |
+
logging.info(
|
| 135 |
+
'Query sequence (sharded db): %s',
|
| 136 |
+
target_sequence
|
| 137 |
+
if len(target_sequence) <= 16
|
| 138 |
+
else f'{target_sequence[:16]}... (len {len(target_sequence)})',
|
| 139 |
+
)
|
| 140 |
+
|
| 141 |
+
global_temp_dir = tempfile.mkdtemp()
|
| 142 |
+
|
| 143 |
+
def _query_shard_fn(
|
| 144 |
+
shard_path: str,
|
| 145 |
+
) -> tuple[msa_tool.MsaToolResult, float]:
|
| 146 |
+
t_start = time.time()
|
| 147 |
+
result = self._query_db_shard(
|
| 148 |
+
target_sequence=target_sequence,
|
| 149 |
+
db_shard_path=shard_path,
|
| 150 |
+
get_tblout=True, # Tblout contains e-values needed for merging.
|
| 151 |
+
global_temp_dir=global_temp_dir,
|
| 152 |
+
)
|
| 153 |
+
return result, time.time() - t_start
|
| 154 |
+
|
| 155 |
+
with futures.ThreadPoolExecutor(max_workers=self._max_threads) as ex:
|
| 156 |
+
tool_outputs, timings = zip(*ex.map(_query_shard_fn, self._shard_paths))
|
| 157 |
+
|
| 158 |
+
logging.info(
|
| 159 |
+
'Finished query for %d shards, shard timings (seconds): %s',
|
| 160 |
+
len(tool_outputs),
|
| 161 |
+
', '.join(f'{t:.1f}' for t in timings),
|
| 162 |
+
)
|
| 163 |
+
|
| 164 |
+
shutil.rmtree(global_temp_dir, ignore_errors=True)
|
| 165 |
+
return _merge_jackhmmer_results(tool_outputs, self._max_sequences)
|
| 166 |
+
|
| 167 |
+
else:
|
| 168 |
+
# Non-sharded case, run the query against the whole database.
|
| 169 |
+
logging.info(
|
| 170 |
+
'Query sequence (non-sharded db): %s',
|
| 171 |
+
target_sequence
|
| 172 |
+
if len(target_sequence) <= 16
|
| 173 |
+
else f'{target_sequence[:16]}... (len {len(target_sequence)})',
|
| 174 |
+
)
|
| 175 |
+
return self._query_db_shard(
|
| 176 |
+
target_sequence=target_sequence,
|
| 177 |
+
db_shard_path=self._database_path,
|
| 178 |
+
get_tblout=False,
|
| 179 |
+
)
|
| 180 |
+
|
| 181 |
+
def _query_db_shard(
|
| 182 |
+
self,
|
| 183 |
+
*,
|
| 184 |
+
target_sequence: str,
|
| 185 |
+
db_shard_path: str,
|
| 186 |
+
get_tblout: bool,
|
| 187 |
+
global_temp_dir: str | None = None,
|
| 188 |
+
) -> msa_tool.MsaToolResult:
|
| 189 |
+
"""Query the database shard using Jackhmmer."""
|
| 190 |
+
|
| 191 |
+
with tempfile.TemporaryDirectory(dir=global_temp_dir) as query_tmp_dir:
|
| 192 |
+
input_fasta_path = os.path.join(query_tmp_dir, 'query.fasta')
|
| 193 |
+
subprocess_utils.create_query_fasta_file(
|
| 194 |
+
sequence=target_sequence, path=input_fasta_path
|
| 195 |
+
)
|
| 196 |
+
output_sto_path = os.path.join(query_tmp_dir, 'output.sto')
|
| 197 |
+
pathlib.Path(output_sto_path).touch()
|
| 198 |
+
|
| 199 |
+
# The F1/F2/F3 are the expected proportion to pass each of the filtering
|
| 200 |
+
# stages (which get progressively more expensive), reducing these
|
| 201 |
+
# speeds up the pipeline at the expensive of sensitivity. They are
|
| 202 |
+
# currently set very low to make querying Mgnify run in a reasonable
|
| 203 |
+
# amount of time.
|
| 204 |
+
cmd_flags = [
|
| 205 |
+
*('-o', '/dev/null'), # Don't pollute stdout with Jackhmmer output.
|
| 206 |
+
*('-A', output_sto_path),
|
| 207 |
+
'--noali',
|
| 208 |
+
*('--F1', str(self._filter_f1)),
|
| 209 |
+
*('--F2', str(self._filter_f2)),
|
| 210 |
+
*('--F3', str(self._filter_f3)),
|
| 211 |
+
*('--cpu', str(self._n_cpu)),
|
| 212 |
+
*('-N', str(self._n_iter)),
|
| 213 |
+
]
|
| 214 |
+
|
| 215 |
+
if get_tblout:
|
| 216 |
+
output_tblout_path = pathlib.Path(query_tmp_dir, 'tblout.txt')
|
| 217 |
+
output_tblout_path.touch()
|
| 218 |
+
cmd_flags.extend(['--tblout', str(output_tblout_path)])
|
| 219 |
+
else:
|
| 220 |
+
output_tblout_path = None
|
| 221 |
+
|
| 222 |
+
# Report only sequences with E-values <= x in per-sequence output.
|
| 223 |
+
if self._e_value is not None:
|
| 224 |
+
cmd_flags.extend(['-E', str(self._e_value)])
|
| 225 |
+
|
| 226 |
+
# Use the same value as the reporting e-value (`-E` flag).
|
| 227 |
+
cmd_flags.extend(['--incE', str(self._e_value)])
|
| 228 |
+
|
| 229 |
+
if self._z_value is not None:
|
| 230 |
+
cmd_flags.extend(['-Z', str(self._z_value)])
|
| 231 |
+
|
| 232 |
+
if self._dom_z_value is not None:
|
| 233 |
+
cmd_flags.extend(['--domZ', str(self._dom_z_value)])
|
| 234 |
+
|
| 235 |
+
if self._dom_e is not None:
|
| 236 |
+
cmd_flags.extend(['--domE', str(self._dom_e)])
|
| 237 |
+
|
| 238 |
+
if self._max_sequences is not None and self._supports_seq_limit:
|
| 239 |
+
cmd_flags.extend(['--seq_limit', str(self._max_sequences)])
|
| 240 |
+
|
| 241 |
+
# The input FASTA and the input db are the last two arguments.
|
| 242 |
+
cmd = [self._binary_path] + cmd_flags + [input_fasta_path, db_shard_path]
|
| 243 |
+
|
| 244 |
+
subprocess_utils.run(
|
| 245 |
+
cmd=cmd,
|
| 246 |
+
cmd_name=f'Jackhmmer ({os.path.basename(db_shard_path)})',
|
| 247 |
+
log_stdout=False,
|
| 248 |
+
log_stderr=True,
|
| 249 |
+
log_on_process_error=True,
|
| 250 |
+
)
|
| 251 |
+
|
| 252 |
+
with open(output_sto_path) as f:
|
| 253 |
+
a3m = parsers.convert_stockholm_to_a3m(
|
| 254 |
+
f, max_sequences=self._max_sequences
|
| 255 |
+
)
|
| 256 |
+
|
| 257 |
+
# Get the tabular output which has e.g. e-value for each target.
|
| 258 |
+
tbl = '' if output_tblout_path is None else output_tblout_path.read_text()
|
| 259 |
+
|
| 260 |
+
return msa_tool.MsaToolResult(
|
| 261 |
+
target_sequence=target_sequence,
|
| 262 |
+
a3m=a3m,
|
| 263 |
+
e_value=self._e_value,
|
| 264 |
+
tblout=tbl,
|
| 265 |
+
)
|
| 266 |
+
|
| 267 |
+
|
| 268 |
+
def _merge_jackhmmer_results(
|
| 269 |
+
jh_results: Sequence[msa_tool.MsaToolResult], max_sequences: int
|
| 270 |
+
) -> msa_tool.MsaToolResult:
|
| 271 |
+
"""Merges Jackhmmer result protos into a single one."""
|
| 272 |
+
assert len(set(jh_res.target_sequence for jh_res in jh_results)) == 1
|
| 273 |
+
assert len(set(jh_res.e_value for jh_res in jh_results)) == 1
|
| 274 |
+
|
| 275 |
+
# Parse the TBL output, create a mapping from hit name to TBL line.
|
| 276 |
+
parsed_tbl = {}
|
| 277 |
+
for jh_result in jh_results:
|
| 278 |
+
assert jh_result.tblout is not None
|
| 279 |
+
for line in jh_result.tblout.splitlines():
|
| 280 |
+
if not line.startswith('#'):
|
| 281 |
+
parsed_tbl[line.partition(' ')[0]] = line
|
| 282 |
+
|
| 283 |
+
# Create an iterator and merge a3m info with tbl info.
|
| 284 |
+
def _merged_a3m_tbl_iter(a3m: str) -> Iterable[tuple[str, str, str, str]]:
|
| 285 |
+
# Don't parse the entire a3m, lazily parse only as many sequences as needed.
|
| 286 |
+
iterator = iter(parsers.lazy_parse_fasta_string(a3m))
|
| 287 |
+
next(iterator) # Skip the query which isn't present in tblout.
|
| 288 |
+
for sequence, description in iterator:
|
| 289 |
+
name = description.partition(' ')[0].partition('/')[0]
|
| 290 |
+
if tbl_info := parsed_tbl.get(name):
|
| 291 |
+
# Skip sequences for which we don't have tbl information.
|
| 292 |
+
yield sequence, description, tbl_info, name
|
| 293 |
+
|
| 294 |
+
def sort_key(seq_data: tuple[str, str, str, str]) -> tuple[float, str]:
|
| 295 |
+
unused_seq, unused_description, tbl_info, name = seq_data
|
| 296 |
+
# Tblout lines have 19 whitespace delimited columns. "-" used if no value
|
| 297 |
+
# present. We want e-value in column with index 4, so do only 5 splits.
|
| 298 |
+
# Use the name in case of a e-value tie.
|
| 299 |
+
return float(tbl_info.split(maxsplit=5)[4]), name
|
| 300 |
+
|
| 301 |
+
# A3M/TBL is sorted by e-value and name, hence we can merge them efficiently.
|
| 302 |
+
merged_a3m_and_tblout = heapq.merge(
|
| 303 |
+
*[_merged_a3m_tbl_iter(res.a3m) for res in jh_results],
|
| 304 |
+
key=sort_key,
|
| 305 |
+
)
|
| 306 |
+
|
| 307 |
+
# Truncate the a3m to max_sequences. Do not truncate the tblout.
|
| 308 |
+
merged_tblout = []
|
| 309 |
+
merged_a3m = [f'>query\n{jh_results[0].target_sequence}']
|
| 310 |
+
for seq, description, tbl_info, _ in merged_a3m_and_tblout:
|
| 311 |
+
merged_tblout.append(tbl_info)
|
| 312 |
+
if len(merged_a3m) < max_sequences:
|
| 313 |
+
merged_a3m.append(f'>{description}\n{seq}')
|
| 314 |
+
|
| 315 |
+
logging.info(
|
| 316 |
+
'Limiting merged MSA depth from %d to %d',
|
| 317 |
+
len(merged_tblout),
|
| 318 |
+
max_sequences,
|
| 319 |
+
)
|
| 320 |
+
|
| 321 |
+
return msa_tool.MsaToolResult(
|
| 322 |
+
target_sequence=jh_results[0].target_sequence,
|
| 323 |
+
a3m='\n'.join(merged_a3m),
|
| 324 |
+
e_value=jh_results[0].e_value,
|
| 325 |
+
tblout=None, # We no longer need the tblout.
|
| 326 |
+
)
|
flax_model/alphafold3/data/tools/mmseqs.py
ADDED
|
@@ -0,0 +1,303 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
|
| 2 |
+
|
| 3 |
+
"""Library to run Mmseqs from Python."""
|
| 4 |
+
|
| 5 |
+
import os
|
| 6 |
+
import tempfile
|
| 7 |
+
import subprocess
|
| 8 |
+
import time
|
| 9 |
+
from typing import List, Dict, Optional
|
| 10 |
+
|
| 11 |
+
|
| 12 |
+
from absl import logging
|
| 13 |
+
from flax_model.alphafold3.data import parsers
|
| 14 |
+
from flax_model.alphafold3.data.tools import msa_tool
|
| 15 |
+
from flax_model.alphafold3.data.tools import subprocess_utils
|
| 16 |
+
import shlex
|
| 17 |
+
|
| 18 |
+
import shutil
|
| 19 |
+
import re
|
| 20 |
+
|
| 21 |
+
|
| 22 |
+
class Mmseqs(msa_tool.MsaTool):
|
| 23 |
+
|
| 24 |
+
_database_types = ["mgnify", "uniprot_cluster_annot", "uniref90", "small_bfd"]
|
| 25 |
+
|
| 26 |
+
def __init__(
|
| 27 |
+
self,
|
| 28 |
+
*,
|
| 29 |
+
binary_path: str,
|
| 30 |
+
database_path: str,
|
| 31 |
+
n_cpu: int = 8,
|
| 32 |
+
e_value: float = 1e-4,
|
| 33 |
+
max_sequences: int = 5000,
|
| 34 |
+
use_gpu: int = 1,
|
| 35 |
+
msa_format_mode: int = 4,
|
| 36 |
+
mmseqs_options: str = "",
|
| 37 |
+
result2msa_options: str = "",
|
| 38 |
+
):
|
| 39 |
+
self.binary_path = binary_path
|
| 40 |
+
self.database_path = database_path
|
| 41 |
+
|
| 42 |
+
subprocess_utils.check_binary_exists(path=self.binary_path, name='MMseqs')
|
| 43 |
+
|
| 44 |
+
if not os.path.exists(self.database_path):
|
| 45 |
+
raise ValueError(f'Database not found: {database_path}')
|
| 46 |
+
|
| 47 |
+
self.n_cpu = n_cpu
|
| 48 |
+
self.e_value = e_value
|
| 49 |
+
self.max_sequences = max_sequences
|
| 50 |
+
self.use_gpu = use_gpu
|
| 51 |
+
self.msa_format_mode = msa_format_mode
|
| 52 |
+
self.mmseqs_options = mmseqs_options
|
| 53 |
+
self.result2msa_options = result2msa_options
|
| 54 |
+
|
| 55 |
+
def _get_gpu_memory_gb(self) -> float:
|
| 56 |
+
hip_devices = os.environ.get('HIP_VISIBLE_DEVICES', '')
|
| 57 |
+
cuda_devices = os.environ.get('CUDA_VISIBLE_DEVICES', '')
|
| 58 |
+
|
| 59 |
+
hy_smi = shutil.which('hy-smi')
|
| 60 |
+
nvidia_smi = shutil.which('nvidia-smi')
|
| 61 |
+
|
| 62 |
+
prefer_hip = bool(hip_devices or hy_smi)
|
| 63 |
+
|
| 64 |
+
device_id: Optional[str] = None
|
| 65 |
+
if prefer_hip and hip_devices:
|
| 66 |
+
ids = [d.strip() for d in hip_devices.split(',') if d.strip()]
|
| 67 |
+
device_id = ids[0] if ids else None
|
| 68 |
+
elif cuda_devices:
|
| 69 |
+
ids = [d.strip() for d in cuda_devices.split(',') if d.strip()]
|
| 70 |
+
device_id = ids[0] if ids else None
|
| 71 |
+
|
| 72 |
+
# 1) hy-smi (HIP path)
|
| 73 |
+
if hy_smi:
|
| 74 |
+
try:
|
| 75 |
+
mem = self._get_gpu_memory_gb_via_hy_smi(hy_smi, device_id)
|
| 76 |
+
if mem is not None:
|
| 77 |
+
logging.info(f"GPU memory (hy-smi): {mem:.1f}GB")
|
| 78 |
+
return mem
|
| 79 |
+
except Exception as e: # noqa: BLE001
|
| 80 |
+
logging.warning(f"hy-smi query failed: {e}")
|
| 81 |
+
|
| 82 |
+
# 2) NVIDIA fallback when CUDA devices are provided
|
| 83 |
+
if nvidia_smi and cuda_devices:
|
| 84 |
+
try:
|
| 85 |
+
ids = [d.strip() for d in cuda_devices.split(',') if d.strip()]
|
| 86 |
+
if ids:
|
| 87 |
+
cmd = [
|
| 88 |
+
nvidia_smi,
|
| 89 |
+
'--query-gpu=memory.total',
|
| 90 |
+
'--format=csv,noheader,nounits',
|
| 91 |
+
f'--id={ids[0]}',
|
| 92 |
+
]
|
| 93 |
+
result = subprocess.run(cmd, capture_output=True, text=True, check=True)
|
| 94 |
+
memory_mb = float(result.stdout.strip())
|
| 95 |
+
return memory_mb / 1024.0
|
| 96 |
+
except (subprocess.CalledProcessError, ValueError, FileNotFoundError) as e:
|
| 97 |
+
logging.warning(f"nvidia-smi query failed: {e}")
|
| 98 |
+
|
| 99 |
+
logging.info("Falling back to default GPU memory: 40GB")
|
| 100 |
+
return 40.0
|
| 101 |
+
|
| 102 |
+
def _parse_memory_gb_from_text(self, text: str) -> Optional[float]:
|
| 103 |
+
units = {
|
| 104 |
+
'gib': 1.0,
|
| 105 |
+
'gb': 1.0,
|
| 106 |
+
'mib': 1.0 / 1024.0,
|
| 107 |
+
'mb': 1.0 / 1024.0,
|
| 108 |
+
'kib': 1.0 / (1024.0 * 1024.0),
|
| 109 |
+
'kb': 1.0 / (1024.0 * 1024.0),
|
| 110 |
+
'b': 1.0 / (1024.0 * 1024.0 * 1024.0),
|
| 111 |
+
}
|
| 112 |
+
pattern = re.compile(r"(\d+(?:\.\d+)?)\s*(GiB|GB|MiB|MB|KiB|KB|B)", re.IGNORECASE)
|
| 113 |
+
candidates = []
|
| 114 |
+
for match in pattern.finditer(text):
|
| 115 |
+
value = float(match.group(1))
|
| 116 |
+
unit = match.group(2).lower()
|
| 117 |
+
factor = units.get(unit, None)
|
| 118 |
+
if factor is not None:
|
| 119 |
+
candidates.append(value * factor)
|
| 120 |
+
if candidates:
|
| 121 |
+
return max(candidates)
|
| 122 |
+
# rocm-smi often prints bytes as: Total Memory (B): 17163091968
|
| 123 |
+
bytes_pattern = re.compile(r"Total\s*Memory\s*\(B\)\s*[:=]\s*(\d{6,})", re.IGNORECASE)
|
| 124 |
+
m = bytes_pattern.search(text)
|
| 125 |
+
if m:
|
| 126 |
+
try:
|
| 127 |
+
return float(m.group(1)) / (1024.0 * 1024.0 * 1024.0)
|
| 128 |
+
except ValueError:
|
| 129 |
+
return None
|
| 130 |
+
# generic Total: <bytes>
|
| 131 |
+
generic_bytes = re.compile(r"Total\s*[:=]\s*(\d{6,})", re.IGNORECASE)
|
| 132 |
+
m2 = generic_bytes.search(text)
|
| 133 |
+
if m2:
|
| 134 |
+
try:
|
| 135 |
+
return float(m2.group(1)) / (1024.0 * 1024.0 * 1024.0)
|
| 136 |
+
except ValueError:
|
| 137 |
+
return None
|
| 138 |
+
return None
|
| 139 |
+
|
| 140 |
+
def _get_gpu_memory_gb_via_hy_smi(self, hy_smi_path: str, device_id: Optional[str]) -> Optional[float]:
|
| 141 |
+
candidate_cmds = []
|
| 142 |
+
# 1) Plain text output exactly as user provided; most reliable for MiB lines
|
| 143 |
+
if device_id is not None:
|
| 144 |
+
candidate_cmds.append([hy_smi_path, '--showmeminfo', 'vram', '-d', str(device_id)])
|
| 145 |
+
candidate_cmds.append([hy_smi_path, '--showmeminfo', 'vram'])
|
| 146 |
+
# 2) CSV/JSON (may not include units next to values, kept as fallback)
|
| 147 |
+
if device_id is not None:
|
| 148 |
+
candidate_cmds.append([hy_smi_path, '--showmeminfo', 'vram', '--csv', '-d', str(device_id)])
|
| 149 |
+
candidate_cmds.append([hy_smi_path, '--showmeminfo', 'vram', '--csv'])
|
| 150 |
+
if device_id is not None:
|
| 151 |
+
candidate_cmds.append([hy_smi_path, '--showmeminfo', 'vram', '--json', '-d', str(device_id)])
|
| 152 |
+
candidate_cmds.append([hy_smi_path, '--showmeminfo', 'vram', '--json'])
|
| 153 |
+
# 3) All-in-one info as a last resort
|
| 154 |
+
candidate_cmds.append([hy_smi_path, '-a'])
|
| 155 |
+
|
| 156 |
+
for cmd in candidate_cmds:
|
| 157 |
+
try:
|
| 158 |
+
result = subprocess.run(cmd, capture_output=True, text=True, check=True)
|
| 159 |
+
# Prefer exact vram parser for hy-smi
|
| 160 |
+
parsed = self._parse_hysmi_vram_total_gb(result.stdout, device_id)
|
| 161 |
+
if parsed is None:
|
| 162 |
+
parsed = self._parse_memory_gb_from_text(result.stdout)
|
| 163 |
+
if parsed is not None and parsed > 0:
|
| 164 |
+
return parsed
|
| 165 |
+
except subprocess.CalledProcessError:
|
| 166 |
+
continue
|
| 167 |
+
return None
|
| 168 |
+
|
| 169 |
+
def _parse_hysmi_vram_total_gb(self, text: str, device_id: Optional[str]) -> Optional[float]:
|
| 170 |
+
# Match lines like: HCU[0] : vram Total Memory (MiB): 65520
|
| 171 |
+
pattern = re.compile(r"(?:DCU|HCU)\[(\d+)\].*?vram\s+Total\s+Memory\s*\(MiB\)\s*:\s*(\d+)", re.IGNORECASE)
|
| 172 |
+
matches = pattern.findall(text)
|
| 173 |
+
if not matches:
|
| 174 |
+
return None
|
| 175 |
+
values_gb = []
|
| 176 |
+
for dev, mib_str in matches:
|
| 177 |
+
try:
|
| 178 |
+
if device_id is not None and str(device_id) != str(dev):
|
| 179 |
+
continue
|
| 180 |
+
mib = float(mib_str)
|
| 181 |
+
values_gb.append(mib / 1024.0)
|
| 182 |
+
except ValueError:
|
| 183 |
+
continue
|
| 184 |
+
if not values_gb:
|
| 185 |
+
return None
|
| 186 |
+
return max(values_gb)
|
| 187 |
+
|
| 188 |
+
|
| 189 |
+
|
| 190 |
+
def _get_database_type(self, database_path: str) -> str:
|
| 191 |
+
path_lower = database_path.lower()
|
| 192 |
+
for db_type in self._database_types:
|
| 193 |
+
if db_type in path_lower:
|
| 194 |
+
return db_type
|
| 195 |
+
return "unknown"
|
| 196 |
+
|
| 197 |
+
def _calculate_memory_allocation(self, database_path: str) -> int:
|
| 198 |
+
db_type = self._get_database_type(database_path)
|
| 199 |
+
if db_type == "unknown":
|
| 200 |
+
return 8
|
| 201 |
+
|
| 202 |
+
if not self.use_gpu:
|
| 203 |
+
return 8
|
| 204 |
+
|
| 205 |
+
gpu_memory_gb = self._get_gpu_memory_gb()
|
| 206 |
+
|
| 207 |
+
available_memory = max(0, gpu_memory_gb)
|
| 208 |
+
|
| 209 |
+
if db_type == "mgnify":
|
| 210 |
+
small_dbs_memory = 3 * 2
|
| 211 |
+
allocated_memory = int(available_memory - small_dbs_memory)
|
| 212 |
+
allocated_memory = max(2, allocated_memory)
|
| 213 |
+
else:
|
| 214 |
+
allocated_memory = 2
|
| 215 |
+
|
| 216 |
+
logging.info(f"Database {db_type}: allocated {allocated_memory}GB GPU memory")
|
| 217 |
+
return allocated_memory
|
| 218 |
+
|
| 219 |
+
def query(self, target_sequence: str) -> msa_tool.MsaToolResult:
|
| 220 |
+
with tempfile.TemporaryDirectory() as tmp_dir:
|
| 221 |
+
input_fasta = os.path.join(tmp_dir, 'query.fasta')
|
| 222 |
+
subprocess_utils.create_query_fasta_file(target_sequence, input_fasta)
|
| 223 |
+
|
| 224 |
+
query_db = os.path.join(tmp_dir, 'queryDB')
|
| 225 |
+
self._run_createdb(input_fasta, query_db)
|
| 226 |
+
|
| 227 |
+
result_db = os.path.join(tmp_dir, 'resultDB')
|
| 228 |
+
self._run_search(query_db, result_db)
|
| 229 |
+
|
| 230 |
+
output_sto = os.path.join(tmp_dir, 'output.sto')
|
| 231 |
+
self._run_result2msa(query_db, result_db, output_sto)
|
| 232 |
+
|
| 233 |
+
with open(output_sto) as f:
|
| 234 |
+
a3m = self._parse_output(f)
|
| 235 |
+
|
| 236 |
+
return msa_tool.MsaToolResult(
|
| 237 |
+
target_sequence=target_sequence,
|
| 238 |
+
a3m=a3m,
|
| 239 |
+
e_value=self.e_value
|
| 240 |
+
)
|
| 241 |
+
|
| 242 |
+
def _run_search(self, input_db: str, output_db: str):
|
| 243 |
+
with tempfile.TemporaryDirectory() as tmp_dir:
|
| 244 |
+
cmd = [
|
| 245 |
+
self.binary_path,
|
| 246 |
+
'search',
|
| 247 |
+
input_db,
|
| 248 |
+
self.database_path,
|
| 249 |
+
output_db,
|
| 250 |
+
tmp_dir,
|
| 251 |
+
'--threads', str(self.n_cpu),
|
| 252 |
+
'-e', str(self.e_value),
|
| 253 |
+
'--gpu', str(self.use_gpu),
|
| 254 |
+
*shlex.split(self.mmseqs_options)
|
| 255 |
+
]
|
| 256 |
+
|
| 257 |
+
if self.use_gpu:
|
| 258 |
+
max_gpu_mem = self._calculate_memory_allocation(self.database_path)
|
| 259 |
+
cmd.extend(['--max-gpu-mem', f'{max_gpu_mem}G'])
|
| 260 |
+
logging.info(f"Running MMseqs search with max GPU memory: {max_gpu_mem}GB")
|
| 261 |
+
else:
|
| 262 |
+
logging.info("Running MMseqs search in CPU mode")
|
| 263 |
+
|
| 264 |
+
subprocess_utils.run(
|
| 265 |
+
cmd=cmd,
|
| 266 |
+
cmd_name='MMseqs2 search',
|
| 267 |
+
log_stderr=True
|
| 268 |
+
)
|
| 269 |
+
|
| 270 |
+
def _run_createdb(self, input_fasta: str, output_db: str):
|
| 271 |
+
cmd = [self.binary_path, 'createdb', input_fasta, output_db]
|
| 272 |
+
subprocess_utils.run(cmd, 'MMseqs2 createdb')
|
| 273 |
+
|
| 274 |
+
def _run_result2msa(self, query_db: str, result_db: str, output_sto: str):
|
| 275 |
+
cmd = [
|
| 276 |
+
self.binary_path,
|
| 277 |
+
'result2msa',
|
| 278 |
+
query_db,
|
| 279 |
+
self.database_path,
|
| 280 |
+
result_db,
|
| 281 |
+
output_sto,
|
| 282 |
+
'--msa-format-mode', str(self.msa_format_mode),
|
| 283 |
+
*shlex.split(self.result2msa_options)
|
| 284 |
+
]
|
| 285 |
+
subprocess_utils.run(cmd, 'MMseqs2 result2msa')
|
| 286 |
+
|
| 287 |
+
def _parse_output(self, file_handle):
|
| 288 |
+
try:
|
| 289 |
+
return parsers.convert_stockholm_to_a3m(file_handle, self.max_sequences)
|
| 290 |
+
except Exception as e:
|
| 291 |
+
logging.warning(f"Stockholm parse failed: {e}")
|
| 292 |
+
|
| 293 |
+
file_handle.seek(0)
|
| 294 |
+
content = file_handle.read()
|
| 295 |
+
debug_file = f"/tmp/stockholm_debug_{int(time.time())}.sto"
|
| 296 |
+
with open(debug_file, 'w') as f:
|
| 297 |
+
f.write(content)
|
| 298 |
+
logging.info(f"Saved debug file: {debug_file}")
|
| 299 |
+
|
| 300 |
+
file_handle.seek(0)
|
| 301 |
+
return parsers.convert_mmseqs_stockholm_to_a3m(file_handle, self.max_sequences)
|
| 302 |
+
|
| 303 |
+
|
flax_model/alphafold3/data/tools/rdkit_utils.py
ADDED
|
@@ -0,0 +1,535 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
|
| 2 |
+
|
| 3 |
+
"""Tools for calculating features for ligands."""
|
| 4 |
+
|
| 5 |
+
import collections
|
| 6 |
+
from collections.abc import Mapping, Sequence
|
| 7 |
+
|
| 8 |
+
from absl import logging
|
| 9 |
+
from flax_model.alphafold3.cpp import cif_dict
|
| 10 |
+
import numpy as np
|
| 11 |
+
import rdkit.Chem as rd_chem
|
| 12 |
+
from rdkit.Chem import AllChem as rd_all_chem
|
| 13 |
+
|
| 14 |
+
|
| 15 |
+
_RDKIT_MMCIF_TO_BOND_TYPE: Mapping[str, rd_chem.BondType] = {
|
| 16 |
+
'SING': rd_chem.BondType.SINGLE,
|
| 17 |
+
'DOUB': rd_chem.BondType.DOUBLE,
|
| 18 |
+
'TRIP': rd_chem.BondType.TRIPLE,
|
| 19 |
+
}
|
| 20 |
+
|
| 21 |
+
_RDKIT_BOND_TYPE_TO_MMCIF: Mapping[rd_chem.BondType, str] = {
|
| 22 |
+
v: k for k, v in _RDKIT_MMCIF_TO_BOND_TYPE.items()
|
| 23 |
+
}
|
| 24 |
+
|
| 25 |
+
_RDKIT_BOND_STEREO_TO_MMCIF: Mapping[rd_chem.BondStereo, str] = {
|
| 26 |
+
rd_chem.BondStereo.STEREONONE: 'N',
|
| 27 |
+
rd_chem.BondStereo.STEREOE: 'E',
|
| 28 |
+
rd_chem.BondStereo.STEREOZ: 'Z',
|
| 29 |
+
rd_chem.BondStereo.STEREOCIS: 'Z',
|
| 30 |
+
rd_chem.BondStereo.STEREOTRANS: 'E',
|
| 31 |
+
}
|
| 32 |
+
|
| 33 |
+
|
| 34 |
+
class MolFromMmcifError(Exception):
|
| 35 |
+
"""Raised when conversion from mmCIF to RDKit Mol fails."""
|
| 36 |
+
|
| 37 |
+
|
| 38 |
+
class UnsupportedMolBondError(Exception):
|
| 39 |
+
"""Raised when we try to handle unsupported RDKit bonds."""
|
| 40 |
+
|
| 41 |
+
|
| 42 |
+
def _populate_atoms_in_mol(
|
| 43 |
+
mol: rd_chem.Mol,
|
| 44 |
+
atom_names: Sequence[str],
|
| 45 |
+
atom_types: Sequence[str],
|
| 46 |
+
atom_charges: Sequence[int],
|
| 47 |
+
implicit_hydrogens: bool,
|
| 48 |
+
ligand_name: str,
|
| 49 |
+
atom_leaving_flags: Sequence[str],
|
| 50 |
+
):
|
| 51 |
+
"""Populate the atoms of a Mol given atom features.
|
| 52 |
+
|
| 53 |
+
Args:
|
| 54 |
+
mol: Mol object.
|
| 55 |
+
atom_names: Names of the atoms.
|
| 56 |
+
atom_types: Types of the atoms.
|
| 57 |
+
atom_charges: Charges of the atoms.
|
| 58 |
+
implicit_hydrogens: Whether to mark the atoms to allow implicit Hs.
|
| 59 |
+
ligand_name: Name of the ligand which the atoms are in.
|
| 60 |
+
atom_leaving_flags: Whether the atom is possibly a leaving atom. Values from
|
| 61 |
+
the CCD column `_chem_comp_atom.pdbx_leaving_atom_flag`. The expected
|
| 62 |
+
values are 'Y' (yes), 'N' (no), '?' (unknown/unset, interpreted as no).
|
| 63 |
+
|
| 64 |
+
Raises:
|
| 65 |
+
ValueError: If atom type is invalid.
|
| 66 |
+
"""
|
| 67 |
+
# Map atom names to the position they will take in the rdkit molecule.
|
| 68 |
+
atom_name_to_idx = {name: i for i, name in enumerate(atom_names)}
|
| 69 |
+
|
| 70 |
+
for atom_name, atom_type, atom_charge, atom_leaving_flag in zip(
|
| 71 |
+
atom_names, atom_types, atom_charges, atom_leaving_flags, strict=True
|
| 72 |
+
):
|
| 73 |
+
try:
|
| 74 |
+
if atom_type == 'X':
|
| 75 |
+
atom_type = '*'
|
| 76 |
+
atom = rd_chem.Atom(atom_type)
|
| 77 |
+
except RuntimeError as e:
|
| 78 |
+
raise ValueError(f'Failed to use atom type: {str(e)}') from e
|
| 79 |
+
|
| 80 |
+
if not implicit_hydrogens:
|
| 81 |
+
atom.SetNoImplicit(True)
|
| 82 |
+
|
| 83 |
+
atom.SetProp('atom_name', atom_name)
|
| 84 |
+
atom.SetProp('atom_leaving_flag', atom_leaving_flag)
|
| 85 |
+
atom.SetFormalCharge(atom_charge)
|
| 86 |
+
residue_info = rd_chem.AtomPDBResidueInfo()
|
| 87 |
+
residue_info.SetName(_format_atom_name(atom_name, atom_type))
|
| 88 |
+
residue_info.SetIsHeteroAtom(True)
|
| 89 |
+
residue_info.SetResidueName(ligand_name)
|
| 90 |
+
residue_info.SetResidueNumber(1)
|
| 91 |
+
atom.SetPDBResidueInfo(residue_info)
|
| 92 |
+
atom_index = mol.AddAtom(atom)
|
| 93 |
+
assert atom_index == atom_name_to_idx[atom_name]
|
| 94 |
+
|
| 95 |
+
|
| 96 |
+
def _populate_bonds_in_mol(
|
| 97 |
+
mol: rd_chem.Mol,
|
| 98 |
+
atom_names: Sequence[str],
|
| 99 |
+
bond_begins: Sequence[str],
|
| 100 |
+
bond_ends: Sequence[str],
|
| 101 |
+
bond_orders: Sequence[str],
|
| 102 |
+
bond_is_aromatics: Sequence[bool],
|
| 103 |
+
):
|
| 104 |
+
"""Populate the bonds of a Mol given bond features.
|
| 105 |
+
|
| 106 |
+
Args:
|
| 107 |
+
mol: Mol object.
|
| 108 |
+
atom_names: Names of atoms in the molecule.
|
| 109 |
+
bond_begins: Names of atoms at the beginning of the bond.
|
| 110 |
+
bond_ends: Names of atoms at the end of the bond.
|
| 111 |
+
bond_orders: What order the bonds are.
|
| 112 |
+
bond_is_aromatics: Whether the bonds are aromatic.
|
| 113 |
+
"""
|
| 114 |
+
atom_name_to_idx = {name: i for i, name in enumerate(atom_names)}
|
| 115 |
+
for begin, end, bond_type, is_aromatic in zip(
|
| 116 |
+
bond_begins, bond_ends, bond_orders, bond_is_aromatics, strict=True
|
| 117 |
+
):
|
| 118 |
+
begin_name, end_name = atom_name_to_idx[begin], atom_name_to_idx[end]
|
| 119 |
+
bond_idx = mol.AddBond(begin_name, end_name, bond_type)
|
| 120 |
+
mol.GetBondWithIdx(bond_idx - 1).SetIsAromatic(is_aromatic)
|
| 121 |
+
|
| 122 |
+
|
| 123 |
+
def sanitize_mol(mol, sort_alphabetically, remove_hydrogens) -> rd_chem.Mol:
|
| 124 |
+
# https://www.rdkit.org/docs/source/rdkit.Chem.rdmolops.html#rdkit.Chem.rdmolops.SanitizeMol
|
| 125 |
+
# Kekulize, check valencies, set aromaticity, conjugation and hybridization.
|
| 126 |
+
# This can repair e.g. incorrect aromatic flags.
|
| 127 |
+
rd_chem.SanitizeMol(mol)
|
| 128 |
+
if sort_alphabetically:
|
| 129 |
+
mol = sort_atoms_by_name(mol)
|
| 130 |
+
if remove_hydrogens:
|
| 131 |
+
mol = rd_chem.RemoveHs(mol)
|
| 132 |
+
return mol
|
| 133 |
+
|
| 134 |
+
|
| 135 |
+
def _add_conformer_to_mol(mol, conformer, force_parse) -> rd_chem.Mol:
|
| 136 |
+
# Create conformer and use it to assign stereochemistry.
|
| 137 |
+
if conformer is not None:
|
| 138 |
+
try:
|
| 139 |
+
mol.AddConformer(conformer)
|
| 140 |
+
rd_chem.AssignStereochemistryFrom3D(mol)
|
| 141 |
+
except ValueError as e:
|
| 142 |
+
logging.warning('Failed to parse conformer: %s', e)
|
| 143 |
+
if not force_parse:
|
| 144 |
+
raise
|
| 145 |
+
|
| 146 |
+
|
| 147 |
+
def mol_from_ccd_cif(
|
| 148 |
+
mol_cif: cif_dict.CifDict,
|
| 149 |
+
*,
|
| 150 |
+
force_parse: bool = False,
|
| 151 |
+
sort_alphabetically: bool = True,
|
| 152 |
+
remove_hydrogens: bool = True,
|
| 153 |
+
implicit_hydrogens: bool = False,
|
| 154 |
+
) -> rd_chem.Mol:
|
| 155 |
+
"""Creates an rdkit Mol object from a CCD mmcif data block.
|
| 156 |
+
|
| 157 |
+
The atoms are renumbered so that their names are in alphabetical order and
|
| 158 |
+
these names are placed on the atoms under property 'atom_name'.
|
| 159 |
+
Only hydrogens which are not required to define the molecule are removed.
|
| 160 |
+
For example, hydrogens that define stereochemistry around a double bond are
|
| 161 |
+
retained.
|
| 162 |
+
See this link for more details.
|
| 163 |
+
https://www.rdkit.org/docs/source/rdkit.Chem.rdmolops.html#rdkit.Chem.rdmolops.RemoveHs
|
| 164 |
+
|
| 165 |
+
Args:
|
| 166 |
+
mol_cif: An mmcif object representing a molecule.
|
| 167 |
+
force_parse: If True, assumes missing aromatic flags are false, substitutes
|
| 168 |
+
deuterium for hydrogen, assumes missing charges are 0 and ignores missing
|
| 169 |
+
conformer / stereochemistry information.
|
| 170 |
+
sort_alphabetically: True: sort atom alphabetically; False: keep CCD order
|
| 171 |
+
remove_hydrogens: if True, remove non-important hydrogens
|
| 172 |
+
implicit_hydrogens: Sets a marker on the atom that allows implicit Hs.
|
| 173 |
+
|
| 174 |
+
Returns:
|
| 175 |
+
An rdkit molecule, with the atoms sorted by name.
|
| 176 |
+
|
| 177 |
+
Raises:
|
| 178 |
+
MolToMmcifError: If conversion from mmcif to rdkit Mol fails. More detailed
|
| 179 |
+
error is available as this error's cause.
|
| 180 |
+
"""
|
| 181 |
+
# Read data fields.
|
| 182 |
+
try:
|
| 183 |
+
atom_names, atom_types, atom_charges, atom_leaving_flags = parse_atom_data(
|
| 184 |
+
mol_cif, force_parse
|
| 185 |
+
)
|
| 186 |
+
bond_begins, bond_ends, bond_orders, bond_is_aromatics = parse_bond_data(
|
| 187 |
+
mol_cif, force_parse
|
| 188 |
+
)
|
| 189 |
+
lig_name = mol_cif['_chem_comp.id'][0].rjust(3)
|
| 190 |
+
except (KeyError, ValueError) as e:
|
| 191 |
+
raise MolFromMmcifError from e
|
| 192 |
+
|
| 193 |
+
# Build Rdkit molecule.
|
| 194 |
+
mol = rd_chem.RWMol()
|
| 195 |
+
|
| 196 |
+
# Per atom features.
|
| 197 |
+
try:
|
| 198 |
+
_populate_atoms_in_mol(
|
| 199 |
+
mol=mol,
|
| 200 |
+
atom_names=atom_names,
|
| 201 |
+
atom_types=atom_types,
|
| 202 |
+
atom_charges=atom_charges,
|
| 203 |
+
implicit_hydrogens=implicit_hydrogens,
|
| 204 |
+
ligand_name=lig_name,
|
| 205 |
+
atom_leaving_flags=atom_leaving_flags,
|
| 206 |
+
)
|
| 207 |
+
except (ValueError, RuntimeError) as e:
|
| 208 |
+
raise MolFromMmcifError from e
|
| 209 |
+
|
| 210 |
+
_populate_bonds_in_mol(
|
| 211 |
+
mol, atom_names, bond_begins, bond_ends, bond_orders, bond_is_aromatics
|
| 212 |
+
)
|
| 213 |
+
|
| 214 |
+
try:
|
| 215 |
+
conformer = _parse_ideal_conformer(mol_cif)
|
| 216 |
+
except (KeyError, ValueError) as e:
|
| 217 |
+
logging.warning('Failed to parse ideal conformer: %s', e)
|
| 218 |
+
if not force_parse:
|
| 219 |
+
raise MolFromMmcifError from e
|
| 220 |
+
conformer = None
|
| 221 |
+
|
| 222 |
+
mol.UpdatePropertyCache(strict=False)
|
| 223 |
+
|
| 224 |
+
try:
|
| 225 |
+
_add_conformer_to_mol(mol, conformer, force_parse)
|
| 226 |
+
mol = sanitize_mol(mol, sort_alphabetically, remove_hydrogens)
|
| 227 |
+
except (
|
| 228 |
+
ValueError,
|
| 229 |
+
rd_chem.KekulizeException,
|
| 230 |
+
rd_chem.AtomValenceException,
|
| 231 |
+
) as e:
|
| 232 |
+
raise MolFromMmcifError from e
|
| 233 |
+
|
| 234 |
+
return mol
|
| 235 |
+
|
| 236 |
+
|
| 237 |
+
def mol_to_ccd_cif(
|
| 238 |
+
mol: rd_chem.Mol,
|
| 239 |
+
component_id: str,
|
| 240 |
+
pdbx_smiles: str | None = None,
|
| 241 |
+
include_hydrogens: bool = True,
|
| 242 |
+
) -> cif_dict.CifDict:
|
| 243 |
+
"""Creates a CCD-like mmcif data block from an rdkit Mol object.
|
| 244 |
+
|
| 245 |
+
Only a subset of associated mmcif fields is populated, but that is
|
| 246 |
+
sufficient for further usage, e.g. in featurization code.
|
| 247 |
+
|
| 248 |
+
Atom names can be specified via `atom_name` property. For atoms with
|
| 249 |
+
unspecified value of that property, the name is assigned based on element type
|
| 250 |
+
and the order in the Mol object.
|
| 251 |
+
|
| 252 |
+
If the Mol object has associated conformers, atom positions from the first of
|
| 253 |
+
them will be populated in the resulting mmcif file.
|
| 254 |
+
|
| 255 |
+
Args:
|
| 256 |
+
mol: An rdkit molecule.
|
| 257 |
+
component_id: Name of the molecule to use in the resulting mmcif. That is
|
| 258 |
+
equivalent to CCD code.
|
| 259 |
+
pdbx_smiles: If specified, the value will be used to populate
|
| 260 |
+
`_chem_comp.pdbx_smiles`.
|
| 261 |
+
include_hydrogens: Whether to include atom and bond data involving
|
| 262 |
+
hydrogens.
|
| 263 |
+
|
| 264 |
+
Returns:
|
| 265 |
+
An mmcif data block corresponding for the given rdkit molecule.
|
| 266 |
+
|
| 267 |
+
Raises:
|
| 268 |
+
UnsupportedMolBond: When a molecule contains a bond that can't be
|
| 269 |
+
represented with mmcif.
|
| 270 |
+
"""
|
| 271 |
+
mol = rd_chem.Mol(mol)
|
| 272 |
+
if include_hydrogens:
|
| 273 |
+
mol = rd_chem.AddHs(mol)
|
| 274 |
+
rd_chem.Kekulize(mol)
|
| 275 |
+
|
| 276 |
+
if mol.GetNumConformers() > 0:
|
| 277 |
+
ideal_conformer = mol.GetConformer(0).GetPositions()
|
| 278 |
+
ideal_conformer = np.vectorize(lambda x: f'{x:.3f}')(ideal_conformer)
|
| 279 |
+
else:
|
| 280 |
+
# No data will be populated in the resulting mmcif if the molecule doesn't
|
| 281 |
+
# have any conformers attached to it.
|
| 282 |
+
ideal_conformer = None
|
| 283 |
+
|
| 284 |
+
mol_cif = collections.defaultdict(list)
|
| 285 |
+
mol_cif['data_'] = [component_id]
|
| 286 |
+
mol_cif['_chem_comp.id'] = [component_id]
|
| 287 |
+
if pdbx_smiles:
|
| 288 |
+
mol_cif['_chem_comp.pdbx_smiles'] = [pdbx_smiles]
|
| 289 |
+
|
| 290 |
+
mol = assign_atom_names_from_graph(mol, keep_existing_names=True)
|
| 291 |
+
|
| 292 |
+
for atom_idx, atom in enumerate(mol.GetAtoms()):
|
| 293 |
+
element = atom.GetSymbol()
|
| 294 |
+
if not include_hydrogens and element in ('H', 'D'):
|
| 295 |
+
continue
|
| 296 |
+
|
| 297 |
+
mol_cif['_chem_comp_atom.comp_id'].append(component_id)
|
| 298 |
+
mol_cif['_chem_comp_atom.atom_id'].append(atom.GetProp('atom_name'))
|
| 299 |
+
mol_cif['_chem_comp_atom.type_symbol'].append(atom.GetSymbol().upper())
|
| 300 |
+
mol_cif['_chem_comp_atom.charge'].append(str(atom.GetFormalCharge()))
|
| 301 |
+
if ideal_conformer is not None:
|
| 302 |
+
coords = ideal_conformer[atom_idx]
|
| 303 |
+
mol_cif['_chem_comp_atom.pdbx_model_Cartn_x_ideal'].append(coords[0])
|
| 304 |
+
mol_cif['_chem_comp_atom.pdbx_model_Cartn_y_ideal'].append(coords[1])
|
| 305 |
+
mol_cif['_chem_comp_atom.pdbx_model_Cartn_z_ideal'].append(coords[2])
|
| 306 |
+
|
| 307 |
+
for bond in mol.GetBonds():
|
| 308 |
+
atom1 = bond.GetBeginAtom()
|
| 309 |
+
atom2 = bond.GetEndAtom()
|
| 310 |
+
if not include_hydrogens and (
|
| 311 |
+
atom1.GetSymbol() in ('H', 'D') or atom2.GetSymbol() in ('H', 'D')
|
| 312 |
+
):
|
| 313 |
+
continue
|
| 314 |
+
mol_cif['_chem_comp_bond.comp_id'].append(component_id)
|
| 315 |
+
mol_cif['_chem_comp_bond.atom_id_1'].append(
|
| 316 |
+
bond.GetBeginAtom().GetProp('atom_name')
|
| 317 |
+
)
|
| 318 |
+
mol_cif['_chem_comp_bond.atom_id_2'].append(
|
| 319 |
+
bond.GetEndAtom().GetProp('atom_name')
|
| 320 |
+
)
|
| 321 |
+
try:
|
| 322 |
+
bond_type = bond.GetBondType()
|
| 323 |
+
# Older versions of RDKit did not have a DATIVE bond type. Convert it to
|
| 324 |
+
# SINGLE to match the AF3 training setup.
|
| 325 |
+
if bond_type == rd_chem.BondType.DATIVE:
|
| 326 |
+
bond_type = rd_chem.BondType.SINGLE
|
| 327 |
+
mol_cif['_chem_comp_bond.value_order'].append(
|
| 328 |
+
_RDKIT_BOND_TYPE_TO_MMCIF[bond_type]
|
| 329 |
+
)
|
| 330 |
+
mol_cif['_chem_comp_bond.pdbx_stereo_config'].append(
|
| 331 |
+
_RDKIT_BOND_STEREO_TO_MMCIF[bond.GetStereo()]
|
| 332 |
+
)
|
| 333 |
+
except KeyError as e:
|
| 334 |
+
raise UnsupportedMolBondError from e
|
| 335 |
+
mol_cif['_chem_comp_bond.pdbx_aromatic_flag'].append(
|
| 336 |
+
'Y' if bond.GetIsAromatic() else 'N'
|
| 337 |
+
)
|
| 338 |
+
|
| 339 |
+
return cif_dict.CifDict(mol_cif)
|
| 340 |
+
|
| 341 |
+
|
| 342 |
+
def _format_atom_name(atom_name: str, atom_type: str) -> str:
|
| 343 |
+
"""Formats an atom name to fit in the four characters specified in PDB.
|
| 344 |
+
|
| 345 |
+
See for example the following note on atom name formatting in PDB files:
|
| 346 |
+
https://www.cgl.ucsf.edu/chimera/docs/UsersGuide/tutorials/pdbintro.html#note1
|
| 347 |
+
|
| 348 |
+
Args:
|
| 349 |
+
atom_name: The unformatted atom name.
|
| 350 |
+
atom_type: The atom element symbol.
|
| 351 |
+
|
| 352 |
+
Returns:
|
| 353 |
+
formatted_atom_name: The formatted 4-character atom name.
|
| 354 |
+
"""
|
| 355 |
+
atom_name = atom_name.strip()
|
| 356 |
+
atom_type = atom_type.strip().upper()
|
| 357 |
+
if len(atom_name) == 1:
|
| 358 |
+
return atom_name.rjust(2).ljust(4)
|
| 359 |
+
elif len(atom_name) == 2:
|
| 360 |
+
if atom_name == atom_type:
|
| 361 |
+
return atom_name.ljust(4)
|
| 362 |
+
return atom_name.center(4)
|
| 363 |
+
elif len(atom_name) == 3:
|
| 364 |
+
if atom_name[:2] == atom_type:
|
| 365 |
+
return atom_name.ljust(4)
|
| 366 |
+
return atom_name.rjust(4)
|
| 367 |
+
elif len(atom_name) == 4:
|
| 368 |
+
return atom_name
|
| 369 |
+
else:
|
| 370 |
+
raise ValueError(
|
| 371 |
+
f'Atom name `{atom_name}` has more than four characters '
|
| 372 |
+
'or is an empty string.'
|
| 373 |
+
)
|
| 374 |
+
|
| 375 |
+
|
| 376 |
+
def parse_atom_data(
|
| 377 |
+
mol_cif: cif_dict.CifDict | Mapping[str, Sequence[str]], force_parse: bool
|
| 378 |
+
) -> tuple[Sequence[str], Sequence[str], Sequence[int], Sequence[str]]:
|
| 379 |
+
"""Parses atoms. If force_parse is True, fix deuterium and missing charge."""
|
| 380 |
+
atom_types = [t.capitalize() for t in mol_cif['_chem_comp_atom.type_symbol']]
|
| 381 |
+
atom_names = mol_cif['_chem_comp_atom.atom_id']
|
| 382 |
+
atom_charges = mol_cif['_chem_comp_atom.charge']
|
| 383 |
+
atom_leaving_flags = ['?'] * len(atom_names)
|
| 384 |
+
if '_chem_comp_atom.pdbx_leaving_atom_flag' in mol_cif:
|
| 385 |
+
atom_leaving_flags = mol_cif['_chem_comp_atom.pdbx_leaving_atom_flag']
|
| 386 |
+
|
| 387 |
+
if force_parse:
|
| 388 |
+
# Replace missing charges with 0.
|
| 389 |
+
atom_charges = [charge if charge != '?' else '0' for charge in atom_charges]
|
| 390 |
+
# Deuterium for hydrogen.
|
| 391 |
+
atom_types = [type_ if type_ != 'D' else 'H' for type_ in atom_types]
|
| 392 |
+
|
| 393 |
+
atom_charges = [int(atom_charge) for atom_charge in atom_charges]
|
| 394 |
+
return atom_names, atom_types, atom_charges, atom_leaving_flags
|
| 395 |
+
|
| 396 |
+
|
| 397 |
+
def parse_bond_data(
|
| 398 |
+
mol_cif: cif_dict.CifDict | Mapping[str, Sequence[str]], force_parse: bool
|
| 399 |
+
) -> tuple[
|
| 400 |
+
Sequence[str], Sequence[str], Sequence[rd_chem.BondType], Sequence[bool]
|
| 401 |
+
]:
|
| 402 |
+
"""Parses bond data. If force_parse is True, ignore missing aromatic flags."""
|
| 403 |
+
# The bond table isn't present if there are no bonds. Use [] in that case.
|
| 404 |
+
begin_atoms = mol_cif.get('_chem_comp_bond.atom_id_1', [])
|
| 405 |
+
end_atoms = mol_cif.get('_chem_comp_bond.atom_id_2', [])
|
| 406 |
+
orders = mol_cif.get('_chem_comp_bond.value_order', [])
|
| 407 |
+
bond_types = [_RDKIT_MMCIF_TO_BOND_TYPE[order] for order in orders]
|
| 408 |
+
|
| 409 |
+
try:
|
| 410 |
+
aromatic_flags = mol_cif.get('_chem_comp_bond.pdbx_aromatic_flag', [])
|
| 411 |
+
is_aromatic = [{'Y': True, 'N': False}[flag] for flag in aromatic_flags]
|
| 412 |
+
except KeyError:
|
| 413 |
+
if force_parse:
|
| 414 |
+
# Set them all to not aromatic.
|
| 415 |
+
is_aromatic = [False for _ in begin_atoms]
|
| 416 |
+
else:
|
| 417 |
+
raise
|
| 418 |
+
|
| 419 |
+
return begin_atoms, end_atoms, bond_types, is_aromatic
|
| 420 |
+
|
| 421 |
+
|
| 422 |
+
def _parse_ideal_conformer(mol_cif: cif_dict.CifDict) -> rd_chem.Conformer:
|
| 423 |
+
"""Builds a conformer containing the ideal coordinates from the CCD.
|
| 424 |
+
|
| 425 |
+
Args:
|
| 426 |
+
mol_cif: An mmcif object representing a molecule.
|
| 427 |
+
|
| 428 |
+
Returns:
|
| 429 |
+
An rdkit conformer filled with the ideal positions from the mmcif.
|
| 430 |
+
|
| 431 |
+
Raises:
|
| 432 |
+
ValueError: if the positions can't be interpreted.
|
| 433 |
+
"""
|
| 434 |
+
atom_x = [
|
| 435 |
+
float(x) for x in mol_cif['_chem_comp_atom.pdbx_model_Cartn_x_ideal']
|
| 436 |
+
]
|
| 437 |
+
atom_y = [
|
| 438 |
+
float(y) for y in mol_cif['_chem_comp_atom.pdbx_model_Cartn_y_ideal']
|
| 439 |
+
]
|
| 440 |
+
atom_z = [
|
| 441 |
+
float(z) for z in mol_cif['_chem_comp_atom.pdbx_model_Cartn_z_ideal']
|
| 442 |
+
]
|
| 443 |
+
atom_positions = zip(atom_x, atom_y, atom_z, strict=True)
|
| 444 |
+
|
| 445 |
+
conformer = rd_chem.Conformer(len(atom_x))
|
| 446 |
+
for atom_index, atom_position in enumerate(atom_positions):
|
| 447 |
+
conformer.SetAtomPosition(atom_index, atom_position)
|
| 448 |
+
|
| 449 |
+
return conformer
|
| 450 |
+
|
| 451 |
+
|
| 452 |
+
def sort_atoms_by_name(mol: rd_chem.Mol) -> rd_chem.Mol:
|
| 453 |
+
"""Sorts the atoms in the molecule by their names."""
|
| 454 |
+
atom_names = {
|
| 455 |
+
atom.GetProp('atom_name'): atom.GetIdx() for atom in mol.GetAtoms()
|
| 456 |
+
}
|
| 457 |
+
|
| 458 |
+
# Sort the name, int tuples by the names.
|
| 459 |
+
sorted_atom_names = sorted(atom_names.items())
|
| 460 |
+
|
| 461 |
+
# Zip these tuples back together to the sorted indices.
|
| 462 |
+
_, new_order = zip(*sorted_atom_names, strict=True)
|
| 463 |
+
|
| 464 |
+
# Reorder the molecule.
|
| 465 |
+
# new_order is effectively an argsort of the names.
|
| 466 |
+
return rd_chem.RenumberAtoms(mol, new_order)
|
| 467 |
+
|
| 468 |
+
|
| 469 |
+
def assign_atom_names_from_graph(
|
| 470 |
+
mol: rd_chem.Mol,
|
| 471 |
+
keep_existing_names: bool = False,
|
| 472 |
+
) -> rd_chem.Mol:
|
| 473 |
+
"""Assigns atom names from the molecular graph.
|
| 474 |
+
|
| 475 |
+
The atom name is stored as an atom property 'atom_name', accessible
|
| 476 |
+
with atom.GetProp('atom_name'). If the property is already specified, and
|
| 477 |
+
keep_existing_names is True we keep the original name.
|
| 478 |
+
|
| 479 |
+
We traverse the graph in the order of the rdkit atom index and give each atom
|
| 480 |
+
a name equal to '{ELEMENT_TYPE}{INDEX}'. E.g. C5 is the name for the fifth
|
| 481 |
+
unnamed carbon encountered.
|
| 482 |
+
|
| 483 |
+
NOTE: A new mol is returned, the original is not changed in place.
|
| 484 |
+
|
| 485 |
+
Args:
|
| 486 |
+
mol: Mol object.
|
| 487 |
+
keep_existing_names: If True, atoms that already have the atom_name property
|
| 488 |
+
will keep their assigned names.
|
| 489 |
+
|
| 490 |
+
Returns:
|
| 491 |
+
A new mol, with potentially new 'atom_name' properties.
|
| 492 |
+
"""
|
| 493 |
+
mol = rd_chem.Mol(mol)
|
| 494 |
+
|
| 495 |
+
specified_atom_names = {
|
| 496 |
+
atom.GetProp('atom_name')
|
| 497 |
+
for atom in mol.GetAtoms()
|
| 498 |
+
if atom.HasProp('atom_name') and keep_existing_names
|
| 499 |
+
}
|
| 500 |
+
|
| 501 |
+
element_counts = collections.Counter()
|
| 502 |
+
for atom in mol.GetAtoms():
|
| 503 |
+
if not atom.HasProp('atom_name') or not keep_existing_names:
|
| 504 |
+
element = atom.GetSymbol()
|
| 505 |
+
while True:
|
| 506 |
+
element_counts[element] += 1
|
| 507 |
+
# Standardize names by using uppercase element type, as in CCD. Only
|
| 508 |
+
# effects elements with more than one letter, e.g. 'Cl' becomes 'CL'.
|
| 509 |
+
new_name = f'{element.upper()}{element_counts[element]}'
|
| 510 |
+
if new_name not in specified_atom_names:
|
| 511 |
+
break
|
| 512 |
+
atom.SetProp('atom_name', new_name)
|
| 513 |
+
|
| 514 |
+
return mol
|
| 515 |
+
|
| 516 |
+
|
| 517 |
+
def get_random_conformer(
|
| 518 |
+
mol: rd_chem.Mol,
|
| 519 |
+
random_seed: int,
|
| 520 |
+
max_iterations: int | None,
|
| 521 |
+
logging_name: str,
|
| 522 |
+
) -> rd_chem.Conformer | None:
|
| 523 |
+
"""Stochastic conformer search method using V3 ETK."""
|
| 524 |
+
params = rd_all_chem.ETKDGv3()
|
| 525 |
+
params.randomSeed = random_seed
|
| 526 |
+
if max_iterations is not None: # Override default value.
|
| 527 |
+
params.maxIterations = max_iterations
|
| 528 |
+
mol_copy = rd_chem.Mol(mol)
|
| 529 |
+
try:
|
| 530 |
+
conformer_id = rd_all_chem.EmbedMolecule(mol_copy, params)
|
| 531 |
+
conformer = mol_copy.GetConformer(conformer_id)
|
| 532 |
+
except ValueError:
|
| 533 |
+
logging.warning('Failed to generate conformer for: %s', logging_name)
|
| 534 |
+
conformer = None
|
| 535 |
+
return conformer
|
flax_model/alphafold3/data/tools/shards.py
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright 2025 DeepMind Technologies Limited
|
| 2 |
+
#
|
| 3 |
+
# AlphaFold 3 source code is licensed under CC BY-NC-SA 4.0. To view a copy of
|
| 4 |
+
# this license, visit https://creativecommons.org/licenses/by-nc-sa/4.0/
|
| 5 |
+
#
|
| 6 |
+
# To request access to the AlphaFold 3 model parameters, follow the process set
|
| 7 |
+
# out at https://github.com/google-deepmind/alphafold3. You may only use these
|
| 8 |
+
# if received directly from Google. Use is subject to terms of use available at
|
| 9 |
+
# https://github.com/google-deepmind/alphafold3/blob/main/WEIGHTS_TERMS_OF_USE.md
|
| 10 |
+
|
| 11 |
+
"""A library to handle shards of the format file_path@NUM_SHARDS.
|
| 12 |
+
|
| 13 |
+
For instance, /path/to/file@20 will generate the following shards:
|
| 14 |
+
|
| 15 |
+
- /path/to/file-00000-of-00020
|
| 16 |
+
- /path/to/file-00001-of-00020
|
| 17 |
+
- ...
|
| 18 |
+
- /path/to/file-00019-of-00020
|
| 19 |
+
|
| 20 |
+
This also supports @* pattern, which will determine the number of shards based
|
| 21 |
+
on the filesystem content.
|
| 22 |
+
"""
|
| 23 |
+
|
| 24 |
+
from collections.abc import Sequence
|
| 25 |
+
import dataclasses
|
| 26 |
+
import pathlib
|
| 27 |
+
import re
|
| 28 |
+
|
| 29 |
+
|
| 30 |
+
_MAX_NUM_SHARDS = 99_999
|
| 31 |
+
_SHARD_RE = re.compile(
|
| 32 |
+
r"""
|
| 33 |
+
^(?P<prefix>[^\?\],\*]+)@
|
| 34 |
+
(?P<shards>(\d{1,5})|\*)
|
| 35 |
+
(?P<suffix>[\._][^\?\]@\*\/]*)?
|
| 36 |
+
$""",
|
| 37 |
+
re.X,
|
| 38 |
+
)
|
| 39 |
+
|
| 40 |
+
|
| 41 |
+
@dataclasses.dataclass(frozen=True)
|
| 42 |
+
class ShardSpec:
|
| 43 |
+
prefix: str
|
| 44 |
+
num_shards: int
|
| 45 |
+
suffix: str
|
| 46 |
+
|
| 47 |
+
|
| 48 |
+
def parse_shard_spec(path: str) -> ShardSpec | None:
|
| 49 |
+
"""Returns the shard spec or None if the path is not a shard spec.
|
| 50 |
+
|
| 51 |
+
For instance, if the shard spec is '/path/to/file@20', the output will be
|
| 52 |
+
('/path/to/file', 20).
|
| 53 |
+
|
| 54 |
+
Args:
|
| 55 |
+
path: the path to parse, e.g. /path/to/file@20 or /path/to/file@*.
|
| 56 |
+
"""
|
| 57 |
+
parsed = re.fullmatch(_SHARD_RE, path)
|
| 58 |
+
if not parsed:
|
| 59 |
+
return None
|
| 60 |
+
prefix = parsed.group('prefix')
|
| 61 |
+
shards = parsed.group('shards')
|
| 62 |
+
suffix = parsed.group('suffix') or ''
|
| 63 |
+
|
| 64 |
+
if shards != '*':
|
| 65 |
+
return ShardSpec(prefix=prefix, num_shards=int(shards), suffix=suffix)
|
| 66 |
+
shard_slice = slice(len(prefix) + 10, len(prefix) + 15)
|
| 67 |
+
shard_path = pathlib.Path(f'{prefix}-00000-of-?????{suffix}')
|
| 68 |
+
for shard in sorted(shard_path.parent.glob(shard_path.name), reverse=True):
|
| 69 |
+
try:
|
| 70 |
+
num_shards = int(str(shard)[shard_slice])
|
| 71 |
+
return ShardSpec(prefix=prefix, num_shards=num_shards, suffix=suffix)
|
| 72 |
+
except ValueError:
|
| 73 |
+
continue
|
| 74 |
+
return None
|
| 75 |
+
|
| 76 |
+
|
| 77 |
+
def get_sharded_paths(shard_spec: str) -> Sequence[str] | None:
|
| 78 |
+
"""Returns a list of file path or None if the input is not a shard spec.
|
| 79 |
+
|
| 80 |
+
Args:
|
| 81 |
+
shard_spec: the specifications of the shard, e.g. /path/to/file@20.
|
| 82 |
+
"""
|
| 83 |
+
parsed_spec = parse_shard_spec(shard_spec)
|
| 84 |
+
if not parsed_spec:
|
| 85 |
+
return None
|
| 86 |
+
|
| 87 |
+
prefix = parsed_spec.prefix
|
| 88 |
+
num_shards = parsed_spec.num_shards
|
| 89 |
+
suffix = parsed_spec.suffix
|
| 90 |
+
if num_shards > _MAX_NUM_SHARDS:
|
| 91 |
+
raise ValueError(f'Shard count for {shard_spec} exceeds {_MAX_NUM_SHARDS}')
|
| 92 |
+
return [
|
| 93 |
+
f'{prefix}-{i:05d}-of-{num_shards:05d}{suffix}' for i in range(num_shards)
|
| 94 |
+
]
|
flax_model/alphafold3/data/tools/subprocess_utils.py
ADDED
|
@@ -0,0 +1,112 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
|
| 2 |
+
|
| 3 |
+
"""Helper functions for launching external tools."""
|
| 4 |
+
|
| 5 |
+
from collections.abc import Sequence
|
| 6 |
+
import os
|
| 7 |
+
import subprocess
|
| 8 |
+
import time
|
| 9 |
+
from typing import Any
|
| 10 |
+
|
| 11 |
+
from absl import logging
|
| 12 |
+
|
| 13 |
+
|
| 14 |
+
def create_query_fasta_file(sequence: str, path: str, linewidth: int = 80):
|
| 15 |
+
"""Creates a fasta file with the sequence with line width limit."""
|
| 16 |
+
with open(path, 'w') as f:
|
| 17 |
+
f.write('>query\n')
|
| 18 |
+
|
| 19 |
+
i = 0
|
| 20 |
+
while i < len(sequence):
|
| 21 |
+
f.write(f'{sequence[i:(i + linewidth)]}\n')
|
| 22 |
+
i += linewidth
|
| 23 |
+
|
| 24 |
+
|
| 25 |
+
def check_binary_exists(path: str, name: str) -> None:
|
| 26 |
+
"""Checks if a binary exists on the given path and raises otherwise."""
|
| 27 |
+
if not os.path.exists(path):
|
| 28 |
+
raise RuntimeError(f'{name} binary not found at {path}')
|
| 29 |
+
|
| 30 |
+
|
| 31 |
+
def jackhmmer_seq_limit_supported(jackhmmer_path: str) -> bool:
|
| 32 |
+
"""Checks if Jackhmmer supports the --seq-limit flag."""
|
| 33 |
+
try:
|
| 34 |
+
subprocess.run(
|
| 35 |
+
[jackhmmer_path, '-h', '--seq_limit', '1'],
|
| 36 |
+
stdout=subprocess.DEVNULL,
|
| 37 |
+
stderr=subprocess.DEVNULL,
|
| 38 |
+
check=True,
|
| 39 |
+
)
|
| 40 |
+
except subprocess.CalledProcessError:
|
| 41 |
+
return False
|
| 42 |
+
return True
|
| 43 |
+
|
| 44 |
+
|
| 45 |
+
def run(
|
| 46 |
+
cmd: Sequence[str],
|
| 47 |
+
cmd_name: str,
|
| 48 |
+
log_on_process_error: bool = False,
|
| 49 |
+
log_stderr: bool = False,
|
| 50 |
+
log_stdout: bool = False,
|
| 51 |
+
max_out_streams_len: int | None = 500_000,
|
| 52 |
+
**run_kwargs,
|
| 53 |
+
) -> subprocess.CompletedProcess[Any]:
|
| 54 |
+
"""Launches a subprocess, times it, and checks for errors.
|
| 55 |
+
|
| 56 |
+
Args:
|
| 57 |
+
cmd: Command to launch.
|
| 58 |
+
cmd_name: Human-readable command name to be used in logs.
|
| 59 |
+
log_on_process_error: Whether to use `logging.error` to log the process'
|
| 60 |
+
stderr on failure.
|
| 61 |
+
log_stderr: Whether to log the stderr of the command.
|
| 62 |
+
log_stdout: Whether to log the stdout of the command.
|
| 63 |
+
max_out_streams_len: Max length of prefix of stdout and stderr included in
|
| 64 |
+
the exception message. Set to `None` to disable truncation.
|
| 65 |
+
**run_kwargs: Any other kwargs for `subprocess.run`.
|
| 66 |
+
|
| 67 |
+
Returns:
|
| 68 |
+
The completed process object.
|
| 69 |
+
|
| 70 |
+
Raises:
|
| 71 |
+
RuntimeError: if the process completes with a non-zero return code.
|
| 72 |
+
"""
|
| 73 |
+
|
| 74 |
+
logging.info('Launching subprocess "%s"', ' '.join(cmd))
|
| 75 |
+
|
| 76 |
+
start_time = time.time()
|
| 77 |
+
try:
|
| 78 |
+
completed_process = subprocess.run(
|
| 79 |
+
cmd,
|
| 80 |
+
check=True,
|
| 81 |
+
stderr=subprocess.PIPE,
|
| 82 |
+
stdout=subprocess.PIPE,
|
| 83 |
+
text=True,
|
| 84 |
+
**run_kwargs,
|
| 85 |
+
)
|
| 86 |
+
except subprocess.CalledProcessError as e:
|
| 87 |
+
if log_on_process_error:
|
| 88 |
+
# Logs have a 15k character limit, so log the error line by line.
|
| 89 |
+
logging.error('%s failed. %s stderr begin:', cmd_name, cmd_name)
|
| 90 |
+
for error_line in e.stderr.splitlines():
|
| 91 |
+
if stripped_error_line := error_line.strip():
|
| 92 |
+
logging.error(stripped_error_line)
|
| 93 |
+
logging.error('%s stderr end.', cmd_name)
|
| 94 |
+
|
| 95 |
+
error_msg = (
|
| 96 |
+
f'{cmd_name} failed'
|
| 97 |
+
f'\nstdout:\n{e.stdout[:max_out_streams_len]}\n'
|
| 98 |
+
f'\nstderr:\n{e.stderr[:max_out_streams_len]}'
|
| 99 |
+
)
|
| 100 |
+
raise RuntimeError(error_msg) from e
|
| 101 |
+
end_time = time.time()
|
| 102 |
+
|
| 103 |
+
logging.info('Finished %s in %.3f seconds', cmd_name, end_time - start_time)
|
| 104 |
+
stdout, stderr = completed_process.stdout, completed_process.stderr
|
| 105 |
+
|
| 106 |
+
if log_stdout and stdout:
|
| 107 |
+
logging.info('%s stdout:\n%s', cmd_name, stdout)
|
| 108 |
+
|
| 109 |
+
if log_stderr and stderr:
|
| 110 |
+
logging.info('%s stderr:\n%s', cmd_name, stderr)
|
| 111 |
+
|
| 112 |
+
return completed_process
|
flax_model/alphafold3/jax/__init__.py
ADDED
|
File without changes
|
flax_model/alphafold3/jax/attention/__init__.py
ADDED
|
File without changes
|
flax_model/alphafold3/jax/attention/attention_base.py
ADDED
|
@@ -0,0 +1,355 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
|
| 2 |
+
|
| 3 |
+
"""Common types and utilities for attention kernels."""
|
| 4 |
+
|
| 5 |
+
import abc
|
| 6 |
+
import dataclasses
|
| 7 |
+
import enum
|
| 8 |
+
import functools
|
| 9 |
+
import math
|
| 10 |
+
from typing import Any, Self
|
| 11 |
+
|
| 12 |
+
from flax_model.alphafold3.jax.common import array_view
|
| 13 |
+
from flax_model.alphafold3.jax.common import precision as precision_lib
|
| 14 |
+
import jax
|
| 15 |
+
import jax.numpy as jnp
|
| 16 |
+
from jax.typing import DTypeLike # pylint: disable=g-importing-member
|
| 17 |
+
import jaxtyping
|
| 18 |
+
from jaxtyping import Array, Bool, Float, Int # pylint: disable=g-multiple-import,g-importing-member
|
| 19 |
+
import typeguard
|
| 20 |
+
|
| 21 |
+
|
| 22 |
+
class AUTO: # Used as a sentinel value.
|
| 23 |
+
pass
|
| 24 |
+
|
| 25 |
+
|
| 26 |
+
DotPrecisionLike = jax.lax.Precision | precision_lib.DotPrecision
|
| 27 |
+
|
| 28 |
+
|
| 29 |
+
@jax.tree_util.register_pytree_node_class
|
| 30 |
+
@dataclasses.dataclass(frozen=True)
|
| 31 |
+
class Mask:
|
| 32 |
+
"""An attention mask.
|
| 33 |
+
|
| 34 |
+
`k_start` (inclusive) and `k_end` (exclusive) define range of enabled
|
| 35 |
+
k-sequence values for each row of logits.
|
| 36 |
+
|
| 37 |
+
For example, a local attention mask could be defined as follows:
|
| 38 |
+
```
|
| 39 |
+
seq_len_q = seq_len_k = 4
|
| 40 |
+
window_size = 2
|
| 41 |
+
k_start = jnp.maximum(0, jnp.arange(seq_len_q) + 1 - window_size)
|
| 42 |
+
mask = Mask(k_start=k_start, is_causal=True)
|
| 43 |
+
assert mask.as_array(seq_len_q, seq_len_k) == jnp.array(
|
| 44 |
+
[[1, 0, 0, 0],
|
| 45 |
+
[1, 1, 0, 0],
|
| 46 |
+
[0, 1, 1, 0],
|
| 47 |
+
[0, 0, 1, 1]], dtype=bool)
|
| 48 |
+
```
|
| 49 |
+
Or equivalently (but less efficiently):
|
| 50 |
+
```
|
| 51 |
+
k_end = jnp.arange(seq_len_q) + 1
|
| 52 |
+
k_start = jnp.maximum(0, k_end - window_size)
|
| 53 |
+
mask = Mask(k_start=k_start, k_end=k_end)
|
| 54 |
+
assert mask.as_array(seq_len_q, seq_len_k) == jnp.array(
|
| 55 |
+
[[1, 0, 0, 0],
|
| 56 |
+
[1, 1, 0, 0],
|
| 57 |
+
[0, 1, 1, 0],
|
| 58 |
+
[0, 0, 1, 1]], dtype=bool)
|
| 59 |
+
```
|
| 60 |
+
|
| 61 |
+
A mask for two independent causal sequences could be defined as follows:
|
| 62 |
+
```
|
| 63 |
+
k_start = jnp.array([0, 0, 2, 2])
|
| 64 |
+
mask = Mask(k_start=k_start, is_causal=True)
|
| 65 |
+
assert mask.as_array(seq_len_q, seq_len_k) == jnp.array(
|
| 66 |
+
[[1, 0, 0, 0],
|
| 67 |
+
[1, 1, 0, 0],
|
| 68 |
+
[0, 0, 1, 0],
|
| 69 |
+
[0, 0, 1, 1]], dtype=bool)
|
| 70 |
+
```
|
| 71 |
+
"""
|
| 72 |
+
|
| 73 |
+
bool_mask: Bool[Array, "*#B #T #t"] | None = None
|
| 74 |
+
_: dataclasses.KW_ONLY
|
| 75 |
+
q_start: Int[Array, "*#B #t"] | None = None
|
| 76 |
+
q_end: Int[Array, "*#B #t"] | None = None
|
| 77 |
+
k_start: Int[Array, "*#B #T"] | None = None
|
| 78 |
+
k_end: Int[Array, "*#B #T"] | None = None
|
| 79 |
+
is_causal: bool = False
|
| 80 |
+
|
| 81 |
+
def tree_flatten(self):
|
| 82 |
+
return (
|
| 83 |
+
self.bool_mask,
|
| 84 |
+
self.q_start,
|
| 85 |
+
self.q_end,
|
| 86 |
+
self.k_start,
|
| 87 |
+
self.k_end,
|
| 88 |
+
), (self.is_causal,)
|
| 89 |
+
|
| 90 |
+
@classmethod
|
| 91 |
+
def tree_unflatten(cls, aux, children) -> Self:
|
| 92 |
+
(is_causal,) = aux
|
| 93 |
+
bool_mask, q_start, q_end, k_start, k_end = children
|
| 94 |
+
return cls(
|
| 95 |
+
bool_mask,
|
| 96 |
+
q_start=q_start,
|
| 97 |
+
q_end=q_end,
|
| 98 |
+
k_start=k_start,
|
| 99 |
+
k_end=k_end,
|
| 100 |
+
is_causal=is_causal,
|
| 101 |
+
)
|
| 102 |
+
|
| 103 |
+
def as_array(
|
| 104 |
+
self,
|
| 105 |
+
q_len_or_indices: int | Int[Array, "*#B T"],
|
| 106 |
+
k_len_or_indices: int | Int[Array, "*#B t"],
|
| 107 |
+
) -> Bool[Array, "*#B #T #t"] | None:
|
| 108 |
+
"""Returns the mask as a boolean array."""
|
| 109 |
+
if isinstance(q_len_or_indices, int):
|
| 110 |
+
q_indices = jnp.arange(q_len_or_indices)
|
| 111 |
+
else:
|
| 112 |
+
q_indices = q_len_or_indices
|
| 113 |
+
|
| 114 |
+
if isinstance(k_len_or_indices, int):
|
| 115 |
+
k_indices = jnp.arange(k_len_or_indices)
|
| 116 |
+
else:
|
| 117 |
+
k_indices = k_len_or_indices
|
| 118 |
+
|
| 119 |
+
q_indices = q_indices[..., None]
|
| 120 |
+
k_indices = k_indices[..., None, :]
|
| 121 |
+
|
| 122 |
+
mask = []
|
| 123 |
+
if self.bool_mask is not None:
|
| 124 |
+
mask.append(self.bool_mask)
|
| 125 |
+
# Check `bool_mask` shape is compatible with `{q,kv}_indices`.
|
| 126 |
+
_ = jnp.broadcast_shapes(
|
| 127 |
+
q_indices.shape, k_indices.shape, self.bool_mask.shape
|
| 128 |
+
)
|
| 129 |
+
|
| 130 |
+
if self.q_start is not None:
|
| 131 |
+
mask.append(q_indices >= self.q_start[..., None, :])
|
| 132 |
+
|
| 133 |
+
if self.q_end is not None:
|
| 134 |
+
mask.append(q_indices < self.q_end[..., None, :])
|
| 135 |
+
|
| 136 |
+
if self.k_start is not None:
|
| 137 |
+
mask.append(k_indices >= self.k_start[..., None])
|
| 138 |
+
|
| 139 |
+
if self.k_end is not None:
|
| 140 |
+
mask.append(k_indices < self.k_end[..., None])
|
| 141 |
+
|
| 142 |
+
if self.is_causal:
|
| 143 |
+
mask.append(q_indices >= k_indices)
|
| 144 |
+
|
| 145 |
+
logical_and = functools.partial(functools.reduce, jnp.logical_and)
|
| 146 |
+
return jax.lax.broadcast_to_rank(logical_and(mask), 3) if mask else None
|
| 147 |
+
|
| 148 |
+
def take(self, *attrs: str) -> tuple[Any, ...]:
|
| 149 |
+
"""Returns a mask with attrs removed and the removed attrs."""
|
| 150 |
+
default_mask = type(self)()
|
| 151 |
+
replacements = {attr: getattr(default_mask, attr) for attr in attrs}
|
| 152 |
+
values = (getattr(self, attr) for attr in attrs)
|
| 153 |
+
return dataclasses.replace(self, **replacements), *values
|
| 154 |
+
|
| 155 |
+
def __and__(self, other: "Bool[Array, '*#B #T #t'] | Mask") -> "Mask": # pylint: disable=g-inconsistent-quotes
|
| 156 |
+
"""Returns the intersection of two masks."""
|
| 157 |
+
if not isinstance(other, Mask):
|
| 158 |
+
other = Mask(other)
|
| 159 |
+
|
| 160 |
+
def combine(op):
|
| 161 |
+
return lambda a, b: b if a is None else a if b is None else op(a, b)
|
| 162 |
+
|
| 163 |
+
return Mask(
|
| 164 |
+
bool_mask=combine(jnp.logical_and)(self.bool_mask, other.bool_mask),
|
| 165 |
+
q_end=combine(jnp.minimum)(self.q_end, other.q_end),
|
| 166 |
+
k_start=combine(jnp.maximum)(self.k_start, other.k_start),
|
| 167 |
+
k_end=combine(jnp.minimum)(self.k_end, other.k_end),
|
| 168 |
+
is_causal=self.is_causal or other.is_causal,
|
| 169 |
+
)
|
| 170 |
+
|
| 171 |
+
|
| 172 |
+
CAUSAL_MASK = Mask(is_causal=True)
|
| 173 |
+
|
| 174 |
+
|
| 175 |
+
SoftmaxResidual = (
|
| 176 |
+
tuple[Float[Array, "*B H T"], Float[Array, "*B H T"]]
|
| 177 |
+
| Float[Array, "*B H T"]
|
| 178 |
+
)
|
| 179 |
+
|
| 180 |
+
|
| 181 |
+
@enum.unique
|
| 182 |
+
class SoftmaxResidualMode(enum.Enum):
|
| 183 |
+
"""The mode of storing softmax residuals for the backwards pass.
|
| 184 |
+
|
| 185 |
+
The stable softmax calculation performs two reductions calculating:
|
| 186 |
+
- the maximum input value (`x_max`),
|
| 187 |
+
- the sum of exponentiated values (`denom`).
|
| 188 |
+
|
| 189 |
+
We can store these values as residuals to avoid the need to recompute them
|
| 190 |
+
in the backwards pass.
|
| 191 |
+
|
| 192 |
+
It is also possible to combine the two residuals into a single residual,
|
| 193 |
+
`res = x_max + log(denom)`, as `exp(x - res) === exp(x - x_max - log(denom))
|
| 194 |
+
=== exp(x - x_max) / denom`. Combining the residuals reduces the memory usage
|
| 195 |
+
of the residuals, but will reduce the accuracy of the backwards pass if
|
| 196 |
+
`abs(x_max) >> log(denom)`.
|
| 197 |
+
"""
|
| 198 |
+
|
| 199 |
+
SEPARATE = "separate"
|
| 200 |
+
COMBINED = "combined"
|
| 201 |
+
|
| 202 |
+
def conform(self, aux: SoftmaxResidual) -> SoftmaxResidual | None:
|
| 203 |
+
match self, aux:
|
| 204 |
+
case None, _:
|
| 205 |
+
return None
|
| 206 |
+
case SoftmaxResidualMode.SEPARATE, (_, _):
|
| 207 |
+
return aux
|
| 208 |
+
case SoftmaxResidualMode.SEPARATE, _: # pytype: disable=redundant-match # b/300135240
|
| 209 |
+
raise ValueError("`aux` has been combined.")
|
| 210 |
+
case SoftmaxResidualMode.COMBINED, (x_max, denom):
|
| 211 |
+
return x_max + jnp.log(denom)
|
| 212 |
+
case SoftmaxResidualMode.COMBINED, _: # pytype: disable=redundant-match # b/300135240
|
| 213 |
+
return aux
|
| 214 |
+
|
| 215 |
+
|
| 216 |
+
class DotProductAttention(abc.ABC):
|
| 217 |
+
"""Dot product attention function."""
|
| 218 |
+
|
| 219 |
+
@jaxtyping.jaxtyped(typechecker=typeguard.typechecked)
|
| 220 |
+
def __call__(
|
| 221 |
+
self,
|
| 222 |
+
query: Float[Array | array_view.ArrayView, "*B T H D"],
|
| 223 |
+
key: Float[Array | array_view.ArrayView, "*B t h D"],
|
| 224 |
+
value: Float[Array | array_view.ArrayView, "*B t h D"],
|
| 225 |
+
*,
|
| 226 |
+
precision: (
|
| 227 |
+
DotPrecisionLike | tuple[DotPrecisionLike, DotPrecisionLike]
|
| 228 |
+
) = jax.lax.Precision.DEFAULT,
|
| 229 |
+
logits_dtype: DTypeLike | type[AUTO] = AUTO,
|
| 230 |
+
bias: Float[Array, "*#B #H #T #t"] | None = None,
|
| 231 |
+
mask: Bool[Array, "*#B #H #T #t"] | Mask | None = None,
|
| 232 |
+
q_indices: Int[Array, "*#B #H T"] | None = None,
|
| 233 |
+
k_indices: Int[Array, "*#B #H t"] | None = None,
|
| 234 |
+
) -> Float[Array, "*B T H D"]:
|
| 235 |
+
"""Performs scaled dot-product attention.
|
| 236 |
+
|
| 237 |
+
Scaled dot-product attention from "Attention is all you need"
|
| 238 |
+
https://arxiv.org/abs/1706.03762.
|
| 239 |
+
|
| 240 |
+
Computes self- or cross-attention. The following is computed:
|
| 241 |
+
softmax(qk_scale * query @ key^T + bias) @ value.
|
| 242 |
+
|
| 243 |
+
Supports both multi-head and multi-query attention
|
| 244 |
+
(https://arxiv.org/abs/1911.02150).
|
| 245 |
+
|
| 246 |
+
Arguments:
|
| 247 |
+
query: Query array of shape `[batch, seq_len_q, num_heads_q, head_dim]`.
|
| 248 |
+
It must be a multiple of num_heads_kv.
|
| 249 |
+
Here's an example of how q/kv heads are interleaved:
|
| 250 |
+
For 8 key/value heads and 4 query heads:
|
| 251 |
+
- key/value heads [0, 1] see query head 0
|
| 252 |
+
- key/value heads [2, 3] see query head 1
|
| 253 |
+
- key/value heads [4, 5] see query head 2
|
| 254 |
+
key: Key array of shape `[batch, seq_len_kv, num_heads_kv, head_dim]`. It
|
| 255 |
+
must be divisible by num_heads_q.
|
| 256 |
+
value: Value array of shape `[batch, seq_len_kv, num_heads_kv, head_dim]`.
|
| 257 |
+
precision: The precision for the dot products. Either a tuple `(
|
| 258 |
+
query_key_dot_precision, weights_value_dot_precision)` or a single
|
| 259 |
+
precision applied to both dot products.
|
| 260 |
+
logits_dtype: Data type for attention logits (`query @ key^T`). If `AUTO`
|
| 261 |
+
is passed (the default), the accumulator type from the `query @ key^T`
|
| 262 |
+
dot product will be used.
|
| 263 |
+
bias: Optional bias array, broadcastable to shape `[batch, num_heads,
|
| 264 |
+
seq_len_q, seq_len_kv]`.
|
| 265 |
+
mask: Optional boolean mask, broadcastable to `[batch, num_heads,
|
| 266 |
+
seq_len_q, seq_len_kv]`. Attention weights are masked out if the
|
| 267 |
+
corresponding mask value is `False`.
|
| 268 |
+
q_indices: Optional indices for each token in query sequence.
|
| 269 |
+
k_indices: Optional indices for each token in key/value sequence.
|
| 270 |
+
|
| 271 |
+
Returns:
|
| 272 |
+
An array with the same shape as `query`.
|
| 273 |
+
""" # fmt: skip
|
| 274 |
+
return self.fwd(
|
| 275 |
+
query,
|
| 276 |
+
key,
|
| 277 |
+
value,
|
| 278 |
+
precision=precision,
|
| 279 |
+
logits_dtype=logits_dtype,
|
| 280 |
+
bias=bias,
|
| 281 |
+
mask=mask,
|
| 282 |
+
q_indices=q_indices,
|
| 283 |
+
k_indices=k_indices,
|
| 284 |
+
)
|
| 285 |
+
|
| 286 |
+
@jaxtyping.jaxtyped(typechecker=typeguard.typechecked)
|
| 287 |
+
def fwd(
|
| 288 |
+
self,
|
| 289 |
+
query: Float[Array | array_view.ArrayView, "*B T H D"],
|
| 290 |
+
key: Float[Array | array_view.ArrayView, "*B t h D"],
|
| 291 |
+
value: Float[Array | array_view.ArrayView, "*B t h D"],
|
| 292 |
+
*,
|
| 293 |
+
precision: (
|
| 294 |
+
DotPrecisionLike | tuple[DotPrecisionLike, DotPrecisionLike]
|
| 295 |
+
) = jax.lax.Precision.DEFAULT,
|
| 296 |
+
logits_dtype: DTypeLike | type[AUTO] = AUTO,
|
| 297 |
+
bias: Float[Array, "*#B #H #T #t"] | None = None,
|
| 298 |
+
mask: Bool[Array, "*#B #H #T #t"] | Mask | None = None,
|
| 299 |
+
q_indices: Int[Array, "*#B #H T"] | None = None,
|
| 300 |
+
k_indices: Int[Array, "*#B #H t"] | None = None,
|
| 301 |
+
) -> Float[Array, "*B T H D"]:
|
| 302 |
+
"""Performs attention."""
|
| 303 |
+
if not isinstance(precision, tuple):
|
| 304 |
+
precision = (precision, precision)
|
| 305 |
+
|
| 306 |
+
q_k_dot_precision, weights_v_dot_precision = precision
|
| 307 |
+
|
| 308 |
+
if not isinstance(q_k_dot_precision, precision_lib.DotPrecision):
|
| 309 |
+
q_k_dot_precision = precision_lib.get_equivalent_dot_precision(
|
| 310 |
+
query.dtype, key.dtype, q_k_dot_precision
|
| 311 |
+
)
|
| 312 |
+
|
| 313 |
+
if not isinstance(weights_v_dot_precision, precision_lib.DotPrecision):
|
| 314 |
+
weights_v_dot_precision = precision_lib.get_equivalent_dot_precision(
|
| 315 |
+
value.dtype, value.dtype, weights_v_dot_precision
|
| 316 |
+
)
|
| 317 |
+
|
| 318 |
+
if logits_dtype is AUTO:
|
| 319 |
+
logits_dtype = q_k_dot_precision.accumulator_dtype
|
| 320 |
+
|
| 321 |
+
if not isinstance(mask, Mask):
|
| 322 |
+
mask = Mask(mask)
|
| 323 |
+
|
| 324 |
+
return self._fwd(
|
| 325 |
+
array_view.as_array_view(query),
|
| 326 |
+
array_view.as_array_view(key),
|
| 327 |
+
array_view.as_array_view(value),
|
| 328 |
+
q_k_dot_precision=q_k_dot_precision,
|
| 329 |
+
logits_dtype=jnp.dtype(logits_dtype),
|
| 330 |
+
logits_scale=1 / math.sqrt(query.shape[-1]),
|
| 331 |
+
bias=bias,
|
| 332 |
+
mask=mask,
|
| 333 |
+
weights_v_dot_precision=weights_v_dot_precision,
|
| 334 |
+
q_indices=q_indices,
|
| 335 |
+
k_indices=k_indices,
|
| 336 |
+
)
|
| 337 |
+
|
| 338 |
+
@abc.abstractmethod
|
| 339 |
+
def _fwd(
|
| 340 |
+
self,
|
| 341 |
+
q: Float[array_view.ArrayView, "*B T H D"],
|
| 342 |
+
k: Float[array_view.ArrayView, "*B t h D"],
|
| 343 |
+
v: Float[array_view.ArrayView, "*B t h D"],
|
| 344 |
+
*,
|
| 345 |
+
q_k_dot_precision: precision_lib.DotPrecision,
|
| 346 |
+
logits_dtype: jnp.dtype,
|
| 347 |
+
logits_scale: float,
|
| 348 |
+
bias: Float[Array, "*#B #H #T #t"] | None,
|
| 349 |
+
mask: Mask | None,
|
| 350 |
+
weights_v_dot_precision: precision_lib.DotPrecision,
|
| 351 |
+
q_indices: Int[Array, "*#B #H T"] | None = None,
|
| 352 |
+
k_indices: Int[Array, "*#B #H t"] | None = None,
|
| 353 |
+
) -> Float[Array, "*B T H D"]:
|
| 354 |
+
"""Performs attention."""
|
| 355 |
+
...
|
flax_model/alphafold3/jax/attention/flash_attention.py
ADDED
|
@@ -0,0 +1,697 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
|
| 2 |
+
|
| 3 |
+
"""Triton FlashAttention implementation."""
|
| 4 |
+
|
| 5 |
+
import dataclasses
|
| 6 |
+
import functools
|
| 7 |
+
|
| 8 |
+
from flax_model.alphafold3.jax.attention import attention_base as base
|
| 9 |
+
from flax_model.alphafold3.jax.common import array_view
|
| 10 |
+
from flax_model.alphafold3.jax.common import precision as precision_lib
|
| 11 |
+
from flax_model.alphafold3.jax.common import triton_utils
|
| 12 |
+
import jax
|
| 13 |
+
import jax.numpy as jnp
|
| 14 |
+
import jax_triton as jt
|
| 15 |
+
import jaxtyping
|
| 16 |
+
from jaxtyping import Array, Bool, Float, Int # pylint: disable=g-multiple-import,g-importing-member
|
| 17 |
+
import triton
|
| 18 |
+
import triton.language as tl
|
| 19 |
+
import typeguard
|
| 20 |
+
|
| 21 |
+
|
| 22 |
+
@triton.jit
|
| 23 |
+
def _fwd_kernel_inner(
|
| 24 |
+
start_loop,
|
| 25 |
+
end_loop,
|
| 26 |
+
q,
|
| 27 |
+
span_q,
|
| 28 |
+
k_block_ptr,
|
| 29 |
+
v_block_ptr,
|
| 30 |
+
bias_block_ptr,
|
| 31 |
+
mask_block_ptr,
|
| 32 |
+
k_start,
|
| 33 |
+
k_end,
|
| 34 |
+
seq_len_k,
|
| 35 |
+
acc,
|
| 36 |
+
m_i,
|
| 37 |
+
l_i,
|
| 38 |
+
bias_advance: tl.constexpr,
|
| 39 |
+
mask_advance: tl.constexpr,
|
| 40 |
+
is_causal: tl.constexpr,
|
| 41 |
+
use_attention_mask: tl.constexpr,
|
| 42 |
+
use_k_start: tl.constexpr,
|
| 43 |
+
use_k_end: tl.constexpr,
|
| 44 |
+
use_bias: tl.constexpr,
|
| 45 |
+
block_k: tl.constexpr,
|
| 46 |
+
use_mask_k: tl.constexpr,
|
| 47 |
+
k_boundary_check: tl.constexpr,
|
| 48 |
+
v_boundary_check: tl.constexpr,
|
| 49 |
+
dot_fn_qk: tl.constexpr,
|
| 50 |
+
dot_fn_kv: tl.constexpr,
|
| 51 |
+
):
|
| 52 |
+
"""Triton MHA forward kernel's inner loop."""
|
| 53 |
+
|
| 54 |
+
for start_k in range(start_loop, end_loop, block_k):
|
| 55 |
+
start_k = tl.multiple_of(start_k, block_k)
|
| 56 |
+
span_k = start_k + tl.arange(0, block_k)
|
| 57 |
+
|
| 58 |
+
k = tl.load(
|
| 59 |
+
k_block_ptr,
|
| 60 |
+
boundary_check=k_boundary_check,
|
| 61 |
+
padding_option="zero" if len(k_boundary_check.value) else "",
|
| 62 |
+
)
|
| 63 |
+
v = tl.load(
|
| 64 |
+
v_block_ptr,
|
| 65 |
+
boundary_check=v_boundary_check,
|
| 66 |
+
padding_option="zero" if len(v_boundary_check.value) else "",
|
| 67 |
+
)
|
| 68 |
+
|
| 69 |
+
if use_bias:
|
| 70 |
+
bias = tl.load(bias_block_ptr)
|
| 71 |
+
|
| 72 |
+
qk = dot_fn_qk(q.to(k.dtype), k) # [block_q, block_k]
|
| 73 |
+
|
| 74 |
+
if use_bias:
|
| 75 |
+
# Prevent dot accumulating into the bias tensor. It appears that Triton
|
| 76 |
+
# doesn't pipeline the bias load as it does the `k` load, so the bias load
|
| 77 |
+
# blocks the matmul if the add is merged.
|
| 78 |
+
qk = qk.to(tl.uint32, bitcast=True) & 0xFFFFFFFF
|
| 79 |
+
qk = qk.to(tl.float32, bitcast=True)
|
| 80 |
+
qk += bias
|
| 81 |
+
|
| 82 |
+
if use_attention_mask | use_k_start | use_k_end:
|
| 83 |
+
mask_value = float(jnp.finfo(jnp.float32).min)
|
| 84 |
+
|
| 85 |
+
if use_attention_mask:
|
| 86 |
+
mask = tl.load(mask_block_ptr)
|
| 87 |
+
qk = tl.where(mask, qk, mask_value)
|
| 88 |
+
|
| 89 |
+
if use_k_start:
|
| 90 |
+
# This check is there to work around a triton compiler bug, but it
|
| 91 |
+
# shouldn't be strictly needed.
|
| 92 |
+
if tl.sum(k_start) != 0:
|
| 93 |
+
qk = tl.where(k_start[:, None] <= span_k[None, :], qk, mask_value)
|
| 94 |
+
if is_causal:
|
| 95 |
+
qk = tl.where(span_q[:, None] >= span_k[None, :], qk, float("-inf"))
|
| 96 |
+
elif use_k_end:
|
| 97 |
+
# When called with k_end and is_causal=True, the causal mask gets folded
|
| 98 |
+
# into k_end and is_causal is set to False.
|
| 99 |
+
qk = tl.where(k_end[:, None] > span_k[None, :], qk, mask_value)
|
| 100 |
+
|
| 101 |
+
if use_mask_k:
|
| 102 |
+
qk = tl.where((span_k < seq_len_k)[None, :], qk, float("-inf"))
|
| 103 |
+
|
| 104 |
+
m_ij = tl.maximum(m_i, tl.max(qk, axis=1)) # Shape [block_q].
|
| 105 |
+
p = tl.exp(qk - m_ij[:, None]) # Shape [block_q, block_k].
|
| 106 |
+
alpha = tl.exp(m_i - m_ij)
|
| 107 |
+
m_i = m_ij
|
| 108 |
+
acc *= alpha[:, None]
|
| 109 |
+
l_i *= alpha
|
| 110 |
+
l_i += tl.sum(p, axis=1)
|
| 111 |
+
|
| 112 |
+
# Add the new block of attention weights.
|
| 113 |
+
acc += dot_fn_kv(p.to(v.dtype), v)
|
| 114 |
+
|
| 115 |
+
k_block_ptr = tl.advance(k_block_ptr, (0, block_k))
|
| 116 |
+
v_block_ptr = tl.advance(v_block_ptr, (block_k, 0))
|
| 117 |
+
bias_block_ptr = tl.advance(bias_block_ptr, bias_advance.value)
|
| 118 |
+
mask_block_ptr = tl.advance(mask_block_ptr, mask_advance.value)
|
| 119 |
+
|
| 120 |
+
return (
|
| 121 |
+
k_block_ptr,
|
| 122 |
+
v_block_ptr,
|
| 123 |
+
bias_block_ptr,
|
| 124 |
+
mask_block_ptr,
|
| 125 |
+
acc,
|
| 126 |
+
m_i,
|
| 127 |
+
l_i,
|
| 128 |
+
)
|
| 129 |
+
|
| 130 |
+
|
| 131 |
+
# Based on Algorithm 1 of https://arxiv.org/abs/2205.14135.
|
| 132 |
+
# Inspired by the official Triton tutorial implementation
|
| 133 |
+
# https://triton-lang.org/main/getting-started/tutorials/06-fused-attention.html
|
| 134 |
+
@triton.jit
|
| 135 |
+
def _fwd_kernel(
|
| 136 |
+
# Input arrays.
|
| 137 |
+
q_ptr,
|
| 138 |
+
k_ptr,
|
| 139 |
+
v_ptr,
|
| 140 |
+
bias_ptr,
|
| 141 |
+
mask_ptr,
|
| 142 |
+
k_start_ptr,
|
| 143 |
+
k_end_ptr,
|
| 144 |
+
# Scalar inputs.
|
| 145 |
+
q_offset,
|
| 146 |
+
k_offset,
|
| 147 |
+
v_offset,
|
| 148 |
+
q_stride_b,
|
| 149 |
+
q_stride_s,
|
| 150 |
+
q_stride_h,
|
| 151 |
+
q_stride_d,
|
| 152 |
+
k_stride_b,
|
| 153 |
+
k_stride_s,
|
| 154 |
+
k_stride_h,
|
| 155 |
+
k_stride_d,
|
| 156 |
+
v_stride_b,
|
| 157 |
+
v_stride_s,
|
| 158 |
+
v_stride_h,
|
| 159 |
+
v_stride_d,
|
| 160 |
+
bias_stride_b,
|
| 161 |
+
bias_stride_h,
|
| 162 |
+
bias_stride_sq,
|
| 163 |
+
bias_stride_sk,
|
| 164 |
+
mask_stride_b,
|
| 165 |
+
mask_stride_h,
|
| 166 |
+
mask_stride_sq,
|
| 167 |
+
mask_stride_sk,
|
| 168 |
+
k_start_stride_b,
|
| 169 |
+
k_start_stride_h,
|
| 170 |
+
k_start_stride_sq,
|
| 171 |
+
k_end_stride_b,
|
| 172 |
+
k_end_stride_h,
|
| 173 |
+
k_end_stride_sq,
|
| 174 |
+
o_stride_b,
|
| 175 |
+
o_stride_s,
|
| 176 |
+
o_stride_h,
|
| 177 |
+
o_stride_d,
|
| 178 |
+
num_heads_q,
|
| 179 |
+
num_heads_k,
|
| 180 |
+
seq_len_q,
|
| 181 |
+
seq_len_k,
|
| 182 |
+
# Output arrays.
|
| 183 |
+
o_ptr,
|
| 184 |
+
# Compile-time constants.
|
| 185 |
+
is_causal: tl.constexpr,
|
| 186 |
+
use_attention_mask: tl.constexpr,
|
| 187 |
+
use_k_start: tl.constexpr,
|
| 188 |
+
use_k_end: tl.constexpr,
|
| 189 |
+
use_bias: tl.constexpr,
|
| 190 |
+
sm_scale: tl.constexpr,
|
| 191 |
+
block_q: tl.constexpr,
|
| 192 |
+
block_k: tl.constexpr,
|
| 193 |
+
head_dim: tl.constexpr,
|
| 194 |
+
use_mask_q: tl.constexpr,
|
| 195 |
+
use_mask_k: tl.constexpr,
|
| 196 |
+
bias_bcast_sq: tl.constexpr,
|
| 197 |
+
mask_bcast_sq: tl.constexpr,
|
| 198 |
+
dot_fn_qk: tl.constexpr,
|
| 199 |
+
dot_fn_kv: tl.constexpr,
|
| 200 |
+
):
|
| 201 |
+
"""Triton MHA forward kernel."""
|
| 202 |
+
# pytype: disable=annotation-type-mismatch,unsupported-operands
|
| 203 |
+
#block_d: tl.constexpr = jt.utils.next_power_of_2(head_dim.value)
|
| 204 |
+
a: tl.constexpr = jt.utils.next_power_of_2(head_dim.value)
|
| 205 |
+
block_d: tl.constexpr = a if a>16 else 32
|
| 206 |
+
|
| 207 |
+
# Each thread block processes one batch element (b) and one head (h).
|
| 208 |
+
start_q = tl.program_id(1) * block_q
|
| 209 |
+
off_h = tl.program_id(0) # int in [0, num_heads_o).
|
| 210 |
+
off_b = tl.program_id(2) # int in [0, batch_size)
|
| 211 |
+
|
| 212 |
+
off_h_k = off_h // (num_heads_q // num_heads_k)
|
| 213 |
+
|
| 214 |
+
q_ptr += off_h * q_stride_h + off_b * q_stride_b + q_offset
|
| 215 |
+
k_ptr += off_h_k * k_stride_h + off_b * k_stride_b + k_offset
|
| 216 |
+
v_ptr += off_h_k * v_stride_h + off_b * v_stride_b + v_offset
|
| 217 |
+
o_ptr += off_h * o_stride_h + off_b * o_stride_b
|
| 218 |
+
|
| 219 |
+
if use_bias:
|
| 220 |
+
bias_ptr += off_b * bias_stride_b + off_h * bias_stride_h
|
| 221 |
+
if use_attention_mask:
|
| 222 |
+
mask_ptr += off_b * mask_stride_b + off_h * mask_stride_h
|
| 223 |
+
if use_k_start:
|
| 224 |
+
k_start_ptr += off_b * k_start_stride_b + off_h * k_start_stride_h
|
| 225 |
+
if use_k_end:
|
| 226 |
+
k_end_ptr += off_b * k_end_stride_b + off_h * k_end_stride_h
|
| 227 |
+
|
| 228 |
+
q_block_ptr = tl.make_block_ptr(
|
| 229 |
+
q_ptr,
|
| 230 |
+
shape=(seq_len_q, head_dim),
|
| 231 |
+
strides=(q_stride_s, q_stride_d),
|
| 232 |
+
offsets=(start_q, 0),
|
| 233 |
+
block_shape=(block_q, block_d),
|
| 234 |
+
order=(1, 0),
|
| 235 |
+
)
|
| 236 |
+
k_block_ptr = tl.make_block_ptr(
|
| 237 |
+
k_ptr,
|
| 238 |
+
shape=(head_dim, seq_len_k),
|
| 239 |
+
strides=(k_stride_d, k_stride_s),
|
| 240 |
+
offsets=(0, 0),
|
| 241 |
+
block_shape=(block_d, block_k),
|
| 242 |
+
order=(0, 1),
|
| 243 |
+
)
|
| 244 |
+
v_block_ptr = tl.make_block_ptr(
|
| 245 |
+
v_ptr,
|
| 246 |
+
shape=(seq_len_k, head_dim),
|
| 247 |
+
strides=(v_stride_s, v_stride_d),
|
| 248 |
+
offsets=(0, 0),
|
| 249 |
+
block_shape=(block_k, block_d),
|
| 250 |
+
order=(1, 0),
|
| 251 |
+
)
|
| 252 |
+
|
| 253 |
+
q_boundary_check0: tl.constexpr = (0,) if use_mask_q else ()
|
| 254 |
+
q_boundary_check1: tl.constexpr = (1,) if head_dim != block_d else ()
|
| 255 |
+
q_boundary_check: tl.constexpr = q_boundary_check0 + q_boundary_check1
|
| 256 |
+
q_padding_option: tl.constexpr = "zero" if len(q_boundary_check.value) else ""
|
| 257 |
+
k_boundary_check: tl.constexpr = (0,) if head_dim != block_d else ()
|
| 258 |
+
v_boundary_check: tl.constexpr = (0,) if use_mask_k else ()
|
| 259 |
+
|
| 260 |
+
# If broadcasting in a given dim, use a 1D block (observed to be faster).
|
| 261 |
+
bias_start_dim: tl.constexpr = 1 if bias_bcast_sq else 0
|
| 262 |
+
bias_block_ptr = tl.make_block_ptr(
|
| 263 |
+
bias_ptr,
|
| 264 |
+
shape=(seq_len_q, seq_len_k)[bias_start_dim:],
|
| 265 |
+
strides=(bias_stride_sq, bias_stride_sk)[bias_start_dim:],
|
| 266 |
+
offsets=(start_q, 0)[bias_start_dim:],
|
| 267 |
+
block_shape=(block_q, block_k)[bias_start_dim:],
|
| 268 |
+
order=(1, 0)[bias_start_dim:],
|
| 269 |
+
)
|
| 270 |
+
bias_advance: tl.constexpr = (0, block_k)[bias_start_dim:]
|
| 271 |
+
|
| 272 |
+
mask_start_dim: tl.constexpr = 1 if mask_bcast_sq else 0
|
| 273 |
+
mask_block_ptr = tl.make_block_ptr(
|
| 274 |
+
mask_ptr,
|
| 275 |
+
shape=(seq_len_q, seq_len_k)[mask_start_dim:],
|
| 276 |
+
strides=(mask_stride_sq, mask_stride_sk)[mask_start_dim:],
|
| 277 |
+
offsets=(start_q, 0)[mask_start_dim:],
|
| 278 |
+
block_shape=(block_q, block_k)[mask_start_dim:],
|
| 279 |
+
order=(1, 0)[mask_start_dim:],
|
| 280 |
+
)
|
| 281 |
+
mask_advance: tl.constexpr = (0, block_k)[mask_start_dim:]
|
| 282 |
+
|
| 283 |
+
k_start_block_ptr = tl.make_block_ptr(
|
| 284 |
+
k_start_ptr,
|
| 285 |
+
shape=(seq_len_q,),
|
| 286 |
+
strides=(k_start_stride_sq,),
|
| 287 |
+
offsets=(start_q,),
|
| 288 |
+
block_shape=(block_q,),
|
| 289 |
+
order=(0,),
|
| 290 |
+
)
|
| 291 |
+
k_end_block_ptr = tl.make_block_ptr(
|
| 292 |
+
k_end_ptr,
|
| 293 |
+
shape=(seq_len_q,),
|
| 294 |
+
strides=(k_end_stride_sq,),
|
| 295 |
+
offsets=(start_q,),
|
| 296 |
+
block_shape=(block_q,),
|
| 297 |
+
order=(0,),
|
| 298 |
+
)
|
| 299 |
+
# pytype: enable=annotation-type-mismatch,unsupported-operands
|
| 300 |
+
|
| 301 |
+
# Each thread block processes a block of block_q queries.
|
| 302 |
+
span_q = start_q + tl.arange(0, block_q)
|
| 303 |
+
|
| 304 |
+
# m_i and l_i (see FlashAttention paper) are updated during the k,v loop.
|
| 305 |
+
m_i = tl.full([block_q], float("-inf"), dtype=tl.float32)
|
| 306 |
+
l_i = tl.zeros([block_q], dtype=tl.float32)
|
| 307 |
+
# acc is the buffer where we accumulate the output on sram.
|
| 308 |
+
acc = tl.zeros([block_q, block_d], dtype=tl.float32)
|
| 309 |
+
|
| 310 |
+
# Load q: it will stay in smem throughout. Indices form a matrix because we
|
| 311 |
+
# read, compute, and write all in 2d chunks. 1 element ~= 1 CUDA thread index.
|
| 312 |
+
q = tl.load(
|
| 313 |
+
q_block_ptr,
|
| 314 |
+
boundary_check=q_boundary_check,
|
| 315 |
+
padding_option=q_padding_option,
|
| 316 |
+
)
|
| 317 |
+
q *= sm_scale
|
| 318 |
+
|
| 319 |
+
# In FlashAttention algorithm 1 there are 2 loops: slow over tiles of kv (size
|
| 320 |
+
# (Bc == block_k here), and fast over blocks of q (size Br == block_q here).
|
| 321 |
+
# Here we only loop over blocks of kv to process entire seq_len, the loop over
|
| 322 |
+
# blocks of q is carried out by the grid.
|
| 323 |
+
k_start = None
|
| 324 |
+
if use_k_start:
|
| 325 |
+
k_start = tl.load(k_start_block_ptr)
|
| 326 |
+
start_loop = tl.maximum(tl.min(k_start), 0)
|
| 327 |
+
blocks_to_skip = start_loop // block_k
|
| 328 |
+
start_loop = block_k * blocks_to_skip # Floor to multiple of block_k.
|
| 329 |
+
for _ in range(blocks_to_skip):
|
| 330 |
+
# Advance all block pointers to the first valid block.
|
| 331 |
+
k_block_ptr = tl.advance(k_block_ptr, (0, block_k))
|
| 332 |
+
v_block_ptr = tl.advance(v_block_ptr, (block_k, 0))
|
| 333 |
+
bias_block_ptr = tl.advance(bias_block_ptr, bias_advance.value)
|
| 334 |
+
mask_block_ptr = tl.advance(mask_block_ptr, mask_advance.value)
|
| 335 |
+
else:
|
| 336 |
+
start_loop = 0
|
| 337 |
+
|
| 338 |
+
k_end = None
|
| 339 |
+
if is_causal:
|
| 340 |
+
end_loop = tl.minimum((start_q // block_k) * block_k, seq_len_k)
|
| 341 |
+
elif use_k_end:
|
| 342 |
+
k_end = tl.load(k_end_block_ptr)
|
| 343 |
+
end_loop = tl.minimum(tl.max(k_end), seq_len_k)
|
| 344 |
+
else:
|
| 345 |
+
end_loop = seq_len_k
|
| 346 |
+
|
| 347 |
+
(
|
| 348 |
+
k_block_ptr,
|
| 349 |
+
v_block_ptr,
|
| 350 |
+
bias_block_ptr,
|
| 351 |
+
mask_block_ptr,
|
| 352 |
+
acc,
|
| 353 |
+
m_i,
|
| 354 |
+
l_i,
|
| 355 |
+
) = _fwd_kernel_inner(
|
| 356 |
+
start_loop,
|
| 357 |
+
end_loop,
|
| 358 |
+
q,
|
| 359 |
+
span_q,
|
| 360 |
+
k_block_ptr,
|
| 361 |
+
v_block_ptr,
|
| 362 |
+
bias_block_ptr,
|
| 363 |
+
mask_block_ptr,
|
| 364 |
+
k_start,
|
| 365 |
+
k_end,
|
| 366 |
+
seq_len_k,
|
| 367 |
+
acc,
|
| 368 |
+
m_i,
|
| 369 |
+
l_i,
|
| 370 |
+
bias_advance,
|
| 371 |
+
mask_advance,
|
| 372 |
+
False, # is_causal
|
| 373 |
+
use_attention_mask,
|
| 374 |
+
use_k_start,
|
| 375 |
+
use_k_end,
|
| 376 |
+
use_bias,
|
| 377 |
+
block_k,
|
| 378 |
+
use_mask_k,
|
| 379 |
+
k_boundary_check,
|
| 380 |
+
v_boundary_check,
|
| 381 |
+
dot_fn_qk,
|
| 382 |
+
dot_fn_kv,
|
| 383 |
+
)
|
| 384 |
+
|
| 385 |
+
if is_causal:
|
| 386 |
+
tl.debug_barrier() # Help compiler schedule loops independently.
|
| 387 |
+
start_loop, end_loop = end_loop, tl.minimum(end_loop + block_k, seq_len_k)
|
| 388 |
+
|
| 389 |
+
_, _, _, _, acc, _, l_i = _fwd_kernel_inner(
|
| 390 |
+
start_loop,
|
| 391 |
+
end_loop,
|
| 392 |
+
q,
|
| 393 |
+
span_q,
|
| 394 |
+
k_block_ptr,
|
| 395 |
+
v_block_ptr,
|
| 396 |
+
bias_block_ptr,
|
| 397 |
+
mask_block_ptr,
|
| 398 |
+
k_start,
|
| 399 |
+
k_end,
|
| 400 |
+
seq_len_k,
|
| 401 |
+
acc,
|
| 402 |
+
m_i,
|
| 403 |
+
l_i,
|
| 404 |
+
bias_advance,
|
| 405 |
+
mask_advance,
|
| 406 |
+
True, # is_causal
|
| 407 |
+
use_attention_mask,
|
| 408 |
+
use_k_start,
|
| 409 |
+
use_k_end,
|
| 410 |
+
use_bias,
|
| 411 |
+
block_k,
|
| 412 |
+
use_mask_k,
|
| 413 |
+
k_boundary_check,
|
| 414 |
+
v_boundary_check,
|
| 415 |
+
dot_fn_qk,
|
| 416 |
+
dot_fn_kv,
|
| 417 |
+
)
|
| 418 |
+
|
| 419 |
+
# It is possible that every value in a row was masked to f32 min or that the
|
| 420 |
+
# main loop has been completely optimised out, and that `l_i` is `0` for that
|
| 421 |
+
# row. Add epsilon value to avoid NaNs from `0 / 0`.
|
| 422 |
+
l_i += float(jnp.finfo(jnp.float32).tiny)
|
| 423 |
+
|
| 424 |
+
acc /= l_i[:, None]
|
| 425 |
+
|
| 426 |
+
# Write output to dram.
|
| 427 |
+
o_block_ptr = tl.make_block_ptr(
|
| 428 |
+
o_ptr,
|
| 429 |
+
shape=(seq_len_q, head_dim),
|
| 430 |
+
strides=(o_stride_s, o_stride_d),
|
| 431 |
+
offsets=(start_q, 0),
|
| 432 |
+
block_shape=(block_q, block_d),
|
| 433 |
+
order=(1, 0),
|
| 434 |
+
)
|
| 435 |
+
acc = acc.to(o_ptr.dtype.element_ty)
|
| 436 |
+
tl.store(o_block_ptr, acc, boundary_check=q_boundary_check)
|
| 437 |
+
|
| 438 |
+
|
| 439 |
+
@jaxtyping.jaxtyped(typechecker=typeguard.typechecked)
|
| 440 |
+
def _fwd(
|
| 441 |
+
q: Float[array_view.ArrayView, "*B T H D"],
|
| 442 |
+
k: Float[array_view.ArrayView, "*B t h D"],
|
| 443 |
+
v: Float[array_view.ArrayView, "*B t h D"],
|
| 444 |
+
bias: Float[Array, "*#B #H #T #t"] | None,
|
| 445 |
+
mask: Bool[Array, "*#B #H #T #t"] | None,
|
| 446 |
+
k_start: Int[Array, "*#B #H #T"] | None,
|
| 447 |
+
k_end: Int[Array, "*#B #H #T"] | None,
|
| 448 |
+
*,
|
| 449 |
+
logits_scale: float,
|
| 450 |
+
is_causal: bool,
|
| 451 |
+
q_k_dot_precision: precision_lib.DotPrecision,
|
| 452 |
+
weights_v_dot_precision: precision_lib.DotPrecision,
|
| 453 |
+
) -> Float[Array, "*B T H D"]:
|
| 454 |
+
"""Forward pass of Triton FlashAttention."""
|
| 455 |
+
|
| 456 |
+
orig_q_shape = q.shape
|
| 457 |
+
q = q.collapse(0, -3, allow_copy=True)
|
| 458 |
+
batch_size, seq_len_q, num_heads_q, head_dim = q.shape
|
| 459 |
+
*_, seq_len_k, num_heads_kv, _ = k.shape
|
| 460 |
+
# Maybe broadcast `k`/`v` heads dimension.
|
| 461 |
+
kv_shape = (batch_size, seq_len_k, num_heads_kv, head_dim)
|
| 462 |
+
k = k.collapse(0, -3, allow_copy=True).broadcast_to(kv_shape)
|
| 463 |
+
v = v.collapse(0, -3, allow_copy=True).broadcast_to(kv_shape)
|
| 464 |
+
|
| 465 |
+
def get_bias_mask_view(x, dtype):
|
| 466 |
+
if x is None:
|
| 467 |
+
x = jnp.array([], dtype=dtype)
|
| 468 |
+
return array_view.ArrayView(x, shape=(0, 0, 0, 0), strides=(0, 0, 0, 0))
|
| 469 |
+
|
| 470 |
+
shape = orig_q_shape[:-3] + (num_heads_q, seq_len_q, seq_len_k)
|
| 471 |
+
return (
|
| 472 |
+
array_view.ArrayView(x)
|
| 473 |
+
.broadcast_to(shape)
|
| 474 |
+
.collapse(0, -3, allow_copy=True)
|
| 475 |
+
)
|
| 476 |
+
|
| 477 |
+
bias = get_bias_mask_view(bias, dtype=q.dtype)
|
| 478 |
+
mask = get_bias_mask_view(mask, dtype=jnp.bool_)
|
| 479 |
+
|
| 480 |
+
def get_range_view(x, seq_len):
|
| 481 |
+
if x is None:
|
| 482 |
+
x = jnp.array([], dtype=jnp.int32)
|
| 483 |
+
return array_view.ArrayView(x, shape=(0, 0, 0), strides=(0, 0, 0))
|
| 484 |
+
|
| 485 |
+
shape = orig_q_shape[:-3] + (num_heads_q, seq_len)
|
| 486 |
+
return (
|
| 487 |
+
array_view.ArrayView(x)
|
| 488 |
+
.broadcast_to(shape)
|
| 489 |
+
.collapse(0, -2, allow_copy=True)
|
| 490 |
+
)
|
| 491 |
+
|
| 492 |
+
k_start = get_range_view(k_start, seq_len_q)
|
| 493 |
+
k_end = get_range_view(k_end, seq_len_q)
|
| 494 |
+
|
| 495 |
+
block_q = 64
|
| 496 |
+
block_k = 64
|
| 497 |
+
|
| 498 |
+
return jt.triton_call(
|
| 499 |
+
q.base,
|
| 500 |
+
k.base,
|
| 501 |
+
v.base,
|
| 502 |
+
bias.base,
|
| 503 |
+
mask.base,
|
| 504 |
+
k_start.base,
|
| 505 |
+
k_end.base,
|
| 506 |
+
q.offset,
|
| 507 |
+
k.offset,
|
| 508 |
+
v.offset,
|
| 509 |
+
*q.strides,
|
| 510 |
+
*k.strides,
|
| 511 |
+
*v.strides,
|
| 512 |
+
*bias.strides,
|
| 513 |
+
*mask.strides,
|
| 514 |
+
k_start.strides,
|
| 515 |
+
k_end.strides,
|
| 516 |
+
*jt.utils.strides_from_shape(q.shape), # out strides.
|
| 517 |
+
num_heads_q,
|
| 518 |
+
num_heads_kv,
|
| 519 |
+
seq_len_q,
|
| 520 |
+
seq_len_k,
|
| 521 |
+
kernel=_fwd_kernel,
|
| 522 |
+
name="triton_flash_attention",
|
| 523 |
+
out_shape=jax.ShapeDtypeStruct(q.shape, q.dtype),
|
| 524 |
+
grid=(num_heads_q, triton.cdiv(seq_len_q, block_q), batch_size),
|
| 525 |
+
num_stages=1,
|
| 526 |
+
num_warps=4,
|
| 527 |
+
is_causal=is_causal,
|
| 528 |
+
use_attention_mask=(mask.size != 0),
|
| 529 |
+
use_k_start=(k_start.size != 0),
|
| 530 |
+
use_k_end=(k_end.size != 0),
|
| 531 |
+
use_bias=(bias.size != 0),
|
| 532 |
+
sm_scale=logits_scale,
|
| 533 |
+
block_q=block_q,
|
| 534 |
+
block_k=block_k,
|
| 535 |
+
head_dim=head_dim,
|
| 536 |
+
use_mask_q=(seq_len_q % block_q != 0),
|
| 537 |
+
use_mask_k=(seq_len_k % block_q != 0),
|
| 538 |
+
bias_bcast_sq=(bias.strides[-2] == 0),
|
| 539 |
+
mask_bcast_sq=(mask.strides[-2] == 0),
|
| 540 |
+
dot_fn_qk=triton_utils.get_tl_dot_fn(q_k_dot_precision),
|
| 541 |
+
dot_fn_kv=triton_utils.get_tl_dot_fn(weights_v_dot_precision),
|
| 542 |
+
).reshape(orig_q_shape)
|
| 543 |
+
|
| 544 |
+
|
| 545 |
+
def _as_batched_array_view(x, axis_size):
|
| 546 |
+
batched_shape = (axis_size,) + x.shape
|
| 547 |
+
batched_strides = (x.base.size // axis_size,) + x.strides
|
| 548 |
+
return dataclasses.replace(x, shape=batched_shape, strides=batched_strides)
|
| 549 |
+
|
| 550 |
+
|
| 551 |
+
def _fwd_vmap_rule(
|
| 552 |
+
axis_size, in_batched, *args, fn: jax.custom_batching.custom_vmap
|
| 553 |
+
):
|
| 554 |
+
"""`vmap` rule for Triton FlashAttention forward op."""
|
| 555 |
+
q, k, v, bias, mask, k_start, k_end = args
|
| 556 |
+
(
|
| 557 |
+
q_batched,
|
| 558 |
+
k_batched,
|
| 559 |
+
v_batched,
|
| 560 |
+
bias_batched,
|
| 561 |
+
mask_batched,
|
| 562 |
+
k_start_batched,
|
| 563 |
+
k_end_batched,
|
| 564 |
+
) = in_batched
|
| 565 |
+
|
| 566 |
+
if q_batched.base:
|
| 567 |
+
q = _as_batched_array_view(q, axis_size)
|
| 568 |
+
if k_batched.base:
|
| 569 |
+
k = _as_batched_array_view(k, axis_size)
|
| 570 |
+
if v_batched.base:
|
| 571 |
+
v = _as_batched_array_view(v, axis_size)
|
| 572 |
+
|
| 573 |
+
# Triton op requires `q`, `k`, `v` batch dims to be identical.
|
| 574 |
+
if q_batched.base and k_batched.base and v_batched.base:
|
| 575 |
+
if bias is not None and not bias_batched:
|
| 576 |
+
bias = jax.lax.broadcast_to_rank(bias, bias.ndim + 1)
|
| 577 |
+
if mask is not None and not mask_batched:
|
| 578 |
+
mask = jax.lax.broadcast_to_rank(mask, mask.ndim + 1)
|
| 579 |
+
if k_start is not None and not k_start_batched:
|
| 580 |
+
k_start = jax.lax.broadcast_to_rank(k_start, k_start.ndim + 1)
|
| 581 |
+
if k_end is not None and not k_end_batched:
|
| 582 |
+
k_end = jax.lax.broadcast_to_rank(k_end, k_end.ndim + 1)
|
| 583 |
+
out = fn(q, k, v, bias, mask, k_start, k_end)
|
| 584 |
+
out_batched = True
|
| 585 |
+
return out, out_batched
|
| 586 |
+
|
| 587 |
+
# Fallback to sequential loop.
|
| 588 |
+
q, k, v = map(jnp.asarray, (q, k, v))
|
| 589 |
+
in_batched = [
|
| 590 |
+
q_batched.base,
|
| 591 |
+
k_batched.base,
|
| 592 |
+
v_batched.base,
|
| 593 |
+
bias_batched,
|
| 594 |
+
mask_batched,
|
| 595 |
+
k_start_batched,
|
| 596 |
+
k_end_batched,
|
| 597 |
+
]
|
| 598 |
+
|
| 599 |
+
def f(q, k, v, *args, **kwargs):
|
| 600 |
+
q, k, v = map(array_view.ArrayView, (q, k, v))
|
| 601 |
+
return fn.fun(q, k, v, *args, **kwargs)
|
| 602 |
+
|
| 603 |
+
sequential_vmap = jax.custom_batching.sequential_vmap(f)
|
| 604 |
+
return sequential_vmap.vmap_rule(axis_size, in_batched, q, k, v, *args[3:])
|
| 605 |
+
|
| 606 |
+
|
| 607 |
+
def _decompose_mask(mask, q, k, q_indices, k_indices):
|
| 608 |
+
"""Decomposes `mask` into a mask array, `is_causal`, `k_start` and `k_end`."""
|
| 609 |
+
if mask is None:
|
| 610 |
+
return None, False, None, None
|
| 611 |
+
|
| 612 |
+
is_causal = False
|
| 613 |
+
k_start = None
|
| 614 |
+
k_end = None
|
| 615 |
+
if q_indices is None and k_indices is None:
|
| 616 |
+
mask, is_causal, k_start, k_end = mask.take("is_causal", "k_start", "k_end")
|
| 617 |
+
if k_start is not None:
|
| 618 |
+
k_start = jax.lax.broadcast_to_rank(k_start, 2)
|
| 619 |
+
if k_end is not None:
|
| 620 |
+
k_end = jax.lax.broadcast_to_rank(k_end, 2)
|
| 621 |
+
if is_causal: # Fold is_causal into k_end
|
| 622 |
+
k_end = jnp.minimum(k_end, jnp.arange(1, q.shape[-3] + 1))
|
| 623 |
+
is_causal = False
|
| 624 |
+
|
| 625 |
+
q_len_or_indices = q.shape[-3] if q_indices is None else q_indices
|
| 626 |
+
k_len_or_indices = k.shape[-3] if k_indices is None else k_indices
|
| 627 |
+
return (
|
| 628 |
+
mask.as_array(q_len_or_indices, k_len_or_indices),
|
| 629 |
+
is_causal,
|
| 630 |
+
k_start,
|
| 631 |
+
k_end,
|
| 632 |
+
)
|
| 633 |
+
|
| 634 |
+
|
| 635 |
+
@dataclasses.dataclass(frozen=True)
|
| 636 |
+
class TritonFlashAttention(base.DotProductAttention):
|
| 637 |
+
"""Triton FlashAttention implementation."""
|
| 638 |
+
|
| 639 |
+
@jaxtyping.jaxtyped(typechecker=typeguard.typechecked)
|
| 640 |
+
def _fwd(
|
| 641 |
+
self,
|
| 642 |
+
q: Float[array_view.ArrayView, "*B T H D"],
|
| 643 |
+
k: Float[array_view.ArrayView, "*B t h D"],
|
| 644 |
+
v: Float[array_view.ArrayView, "*B t h D"],
|
| 645 |
+
bias: Float[Array, "*#B #H #T #t"] | None,
|
| 646 |
+
*,
|
| 647 |
+
q_k_dot_precision: precision_lib.DotPrecision,
|
| 648 |
+
logits_dtype: jnp.dtype,
|
| 649 |
+
logits_scale: float,
|
| 650 |
+
mask: base.Mask | None,
|
| 651 |
+
weights_v_dot_precision: precision_lib.DotPrecision,
|
| 652 |
+
q_indices: Int[Array, "*#B #H T"] | None = None,
|
| 653 |
+
k_indices: Int[Array, "*#B #H t"] | None = None,
|
| 654 |
+
) -> Float[Array, "*B T H D"]:
|
| 655 |
+
if logits_dtype != jnp.float32:
|
| 656 |
+
raise ValueError("`logits_dtype` must be float32.")
|
| 657 |
+
|
| 658 |
+
kwargs = dict(
|
| 659 |
+
logits_scale=logits_scale,
|
| 660 |
+
q_k_dot_precision=q_k_dot_precision,
|
| 661 |
+
weights_v_dot_precision=weights_v_dot_precision,
|
| 662 |
+
)
|
| 663 |
+
|
| 664 |
+
def attend_fwd(
|
| 665 |
+
q,
|
| 666 |
+
k,
|
| 667 |
+
v,
|
| 668 |
+
bias,
|
| 669 |
+
mask_,
|
| 670 |
+
q_indices,
|
| 671 |
+
k_indices,
|
| 672 |
+
):
|
| 673 |
+
|
| 674 |
+
mask, is_causal, k_start, k_end = _decompose_mask(
|
| 675 |
+
mask_, q, k, q_indices, k_indices
|
| 676 |
+
)
|
| 677 |
+
|
| 678 |
+
fwd_closed_kwargs = dict(
|
| 679 |
+
is_causal=is_causal,
|
| 680 |
+
**kwargs,
|
| 681 |
+
)
|
| 682 |
+
|
| 683 |
+
fwd_closed = functools.partial(_fwd, **fwd_closed_kwargs)
|
| 684 |
+
fwd_closed = jax.custom_batching.custom_vmap(fwd_closed)
|
| 685 |
+
fwd_closed.def_vmap(functools.partial(_fwd_vmap_rule, fn=fwd_closed))
|
| 686 |
+
|
| 687 |
+
return fwd_closed(q, k, v, bias, mask, k_start, k_end)
|
| 688 |
+
|
| 689 |
+
return attend_fwd(
|
| 690 |
+
q,
|
| 691 |
+
k,
|
| 692 |
+
v,
|
| 693 |
+
bias,
|
| 694 |
+
mask,
|
| 695 |
+
q_indices,
|
| 696 |
+
k_indices,
|
| 697 |
+
)
|
flax_model/alphafold3/jax/attention/xla_attention.py
ADDED
|
@@ -0,0 +1,132 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
|
| 2 |
+
|
| 3 |
+
"""XLA implementation of scaled dot-product attention."""
|
| 4 |
+
|
| 5 |
+
import dataclasses
|
| 6 |
+
|
| 7 |
+
from flax_model.alphafold3.jax.attention import attention_base as base
|
| 8 |
+
from flax_model.alphafold3.jax.common import array_view
|
| 9 |
+
from flax_model.alphafold3.jax.common import precision as precision_lib
|
| 10 |
+
import jax
|
| 11 |
+
import jax.numpy as jnp
|
| 12 |
+
import jaxtyping
|
| 13 |
+
from jaxtyping import Array, Float, Int # pylint: disable=g-multiple-import,g-importing-member
|
| 14 |
+
import typeguard
|
| 15 |
+
|
| 16 |
+
|
| 17 |
+
def _get_precision(
|
| 18 |
+
backend: str, precision: precision_lib.DotPrecision
|
| 19 |
+
) -> jax.lax.Precision:
|
| 20 |
+
if backend == "gpu" and precision == precision_lib.DotPrecision.F32_F32:
|
| 21 |
+
return jax.lax.Precision.HIGHEST
|
| 22 |
+
return jax.lax.Precision.DEFAULT
|
| 23 |
+
|
| 24 |
+
|
| 25 |
+
def einsum_with_dot_precision(
|
| 26 |
+
subscript: str,
|
| 27 |
+
a: jax.Array,
|
| 28 |
+
b: jax.Array,
|
| 29 |
+
*,
|
| 30 |
+
precision: precision_lib.DotPrecision,
|
| 31 |
+
) -> jax.Array:
|
| 32 |
+
"""Evaluate `fn` with the given precision."""
|
| 33 |
+
result = jnp.einsum(
|
| 34 |
+
subscript,
|
| 35 |
+
a.astype(precision.operand_dtype),
|
| 36 |
+
b.astype(precision.operand_dtype),
|
| 37 |
+
precision=_get_precision(jax.default_backend().lower(), precision),
|
| 38 |
+
preferred_element_type=precision.accumulator_dtype,
|
| 39 |
+
)
|
| 40 |
+
assert result.dtype == precision.accumulator_dtype
|
| 41 |
+
return result
|
| 42 |
+
|
| 43 |
+
|
| 44 |
+
def _softmax(x: jax.Array) -> jax.Array:
|
| 45 |
+
"""Computes softmax."""
|
| 46 |
+
# Always perform reductions in at least f32 precision.
|
| 47 |
+
dtype = jnp.promote_types(x.dtype, jnp.float32)
|
| 48 |
+
x_max = jnp.max(x.astype(dtype), axis=-1, keepdims=True)
|
| 49 |
+
unnormalized = jnp.exp(x - x_max)
|
| 50 |
+
denom = jnp.sum(unnormalized, axis=-1, keepdims=True)
|
| 51 |
+
return unnormalized / denom
|
| 52 |
+
|
| 53 |
+
|
| 54 |
+
@jaxtyping.jaxtyped(typechecker=typeguard.typechecked)
|
| 55 |
+
def _attend(
|
| 56 |
+
q: Float[array_view.ArrayView, "*B T H D"],
|
| 57 |
+
k: Float[array_view.ArrayView, "*B t #H D"],
|
| 58 |
+
v: Float[array_view.ArrayView, "*B t #H D"],
|
| 59 |
+
*,
|
| 60 |
+
q_k_dot_precision: precision_lib.DotPrecision,
|
| 61 |
+
logits_dtype: jnp.dtype,
|
| 62 |
+
logits_scale: float,
|
| 63 |
+
bias: Float[Array, "*#B #H #T #t"] | None,
|
| 64 |
+
mask: base.Mask | None,
|
| 65 |
+
weights_v_dot_precision: precision_lib.DotPrecision,
|
| 66 |
+
q_indices: Int[Array, "*#B #H T"] | None,
|
| 67 |
+
k_indices: Int[Array, "*#B #H t"] | None,
|
| 68 |
+
) -> Float[Array, "*B T H D"]:
|
| 69 |
+
"""Computes attention."""
|
| 70 |
+
logits = einsum_with_dot_precision(
|
| 71 |
+
"...qhd,...khd->...hqk", q, k, precision=q_k_dot_precision
|
| 72 |
+
).astype(logits_dtype)
|
| 73 |
+
|
| 74 |
+
logits *= logits_scale
|
| 75 |
+
|
| 76 |
+
if bias is not None:
|
| 77 |
+
logits += bias
|
| 78 |
+
|
| 79 |
+
if mask is not None:
|
| 80 |
+
q_len_or_indices = q.shape[-3] if q_indices is None else q_indices
|
| 81 |
+
k_len_or_indices = k.shape[-3] if k_indices is None else k_indices
|
| 82 |
+
mask = mask.as_array(q_len_or_indices, k_len_or_indices)
|
| 83 |
+
|
| 84 |
+
if mask is not None:
|
| 85 |
+
mask_value = float(jnp.finfo(logits.dtype).min)
|
| 86 |
+
|
| 87 |
+
logits = jnp.where(jnp.asarray(mask), logits, mask_value)
|
| 88 |
+
|
| 89 |
+
weights = _softmax(logits)
|
| 90 |
+
|
| 91 |
+
weights = weights.astype(v.dtype)
|
| 92 |
+
out = einsum_with_dot_precision(
|
| 93 |
+
"...hqk,...khd->...qhd", weights, v, precision=weights_v_dot_precision
|
| 94 |
+
).astype(q.dtype)
|
| 95 |
+
return out
|
| 96 |
+
|
| 97 |
+
|
| 98 |
+
@dataclasses.dataclass(frozen=True)
|
| 99 |
+
class XlaDotProductAttention(base.DotProductAttention):
|
| 100 |
+
"""XLA dot product attention function."""
|
| 101 |
+
|
| 102 |
+
_: dataclasses.KW_ONLY
|
| 103 |
+
|
| 104 |
+
def _fwd(
|
| 105 |
+
self,
|
| 106 |
+
q: Float[array_view.ArrayView, "*B T H D"],
|
| 107 |
+
k: Float[array_view.ArrayView, "*B t #H D"],
|
| 108 |
+
v: Float[array_view.ArrayView, "*B t #H D"],
|
| 109 |
+
*,
|
| 110 |
+
q_k_dot_precision: precision_lib.DotPrecision,
|
| 111 |
+
logits_dtype: jnp.dtype,
|
| 112 |
+
logits_scale: float,
|
| 113 |
+
bias: Float[Array, "*#B #H #T #t"] | None,
|
| 114 |
+
mask: base.Mask | None,
|
| 115 |
+
weights_v_dot_precision: precision_lib.DotPrecision,
|
| 116 |
+
q_indices: Int[Array, "*#B #H T"] | None = None,
|
| 117 |
+
k_indices: Int[Array, "*#B #H t"] | None = None,
|
| 118 |
+
) -> Float[Array, "*B T H D"]:
|
| 119 |
+
|
| 120 |
+
return _attend(
|
| 121 |
+
q,
|
| 122 |
+
k,
|
| 123 |
+
v,
|
| 124 |
+
bias=bias,
|
| 125 |
+
mask=mask,
|
| 126 |
+
q_indices=q_indices,
|
| 127 |
+
k_indices=k_indices,
|
| 128 |
+
q_k_dot_precision=q_k_dot_precision,
|
| 129 |
+
logits_dtype=logits_dtype,
|
| 130 |
+
logits_scale=logits_scale,
|
| 131 |
+
weights_v_dot_precision=weights_v_dot_precision,
|
| 132 |
+
)
|
flax_model/alphafold3/jax/common/__init__.py
ADDED
|
File without changes
|
flax_model/alphafold3/jax/common/triton_utils.py
ADDED
|
@@ -0,0 +1,118 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
|
| 2 |
+
|
| 3 |
+
"""Triton utils."""
|
| 4 |
+
|
| 5 |
+
from collections.abc import Callable, Mapping
|
| 6 |
+
|
| 7 |
+
from flax_model.alphafold3.jax.common import precision as precision_lib
|
| 8 |
+
import jax
|
| 9 |
+
import jax.numpy as jnp
|
| 10 |
+
import triton
|
| 11 |
+
import triton.language as tl
|
| 12 |
+
|
| 13 |
+
|
| 14 |
+
_JNP_TO_TL_DTYPES: Mapping[jnp.dtype, tl.dtype] = {
|
| 15 |
+
jnp.bool_: tl.int1,
|
| 16 |
+
jnp.int8: tl.int8,
|
| 17 |
+
jnp.int16: tl.int16,
|
| 18 |
+
jnp.int32: tl.int32,
|
| 19 |
+
jnp.int64: tl.int64,
|
| 20 |
+
jnp.uint8: tl.uint8,
|
| 21 |
+
jnp.uint16: tl.uint16,
|
| 22 |
+
jnp.uint32: tl.uint32,
|
| 23 |
+
jnp.uint64: tl.uint64,
|
| 24 |
+
jnp.float16: tl.float16,
|
| 25 |
+
jnp.bfloat16: tl.bfloat16,
|
| 26 |
+
jnp.float32: tl.float32,
|
| 27 |
+
jnp.float64: tl.float64,
|
| 28 |
+
}
|
| 29 |
+
|
| 30 |
+
|
| 31 |
+
def jnp_to_tl_dtype(jnp_dtype: jnp.dtype) -> tl.dtype:
|
| 32 |
+
return _JNP_TO_TL_DTYPES[jnp_dtype]
|
| 33 |
+
|
| 34 |
+
|
| 35 |
+
def get_tl_dot_fn(
|
| 36 |
+
precision: precision_lib.DotPrecision,
|
| 37 |
+
) -> Callable[..., tl.tensor]:
|
| 38 |
+
"""Returns a tl `dot` implementation with the specified precision.
|
| 39 |
+
|
| 40 |
+
Args:
|
| 41 |
+
precision: The `dot` precision.
|
| 42 |
+
"""
|
| 43 |
+
if not is_precision_supported(precision):
|
| 44 |
+
raise ValueError(f'Unsupported dot precision: {precision}')
|
| 45 |
+
|
| 46 |
+
if precision == precision_lib.DotPrecision.TF32_F32_3X:
|
| 47 |
+
return _dot_tf32_f32_3x
|
| 48 |
+
|
| 49 |
+
in_dtype = jnp_to_tl_dtype(precision.operand_dtype)
|
| 50 |
+
out_dtype = jnp_to_tl_dtype(precision.accumulator_dtype)
|
| 51 |
+
allow_tf32 = precision == precision_lib.DotPrecision.TF32_F32
|
| 52 |
+
|
| 53 |
+
@tl.core.extern
|
| 54 |
+
def _dot_fn(
|
| 55 |
+
a: tl.core.tensor,
|
| 56 |
+
b: tl.core.tensor,
|
| 57 |
+
*,
|
| 58 |
+
trans_a: bool = False,
|
| 59 |
+
trans_b: bool = False,
|
| 60 |
+
_builder,
|
| 61 |
+
):
|
| 62 |
+
if in_dtype == tl.float32:
|
| 63 |
+
tl.static_assert(a.dtype == tl.float32, _builder=_builder)
|
| 64 |
+
tl.static_assert(b.dtype == tl.float32, _builder=_builder)
|
| 65 |
+
else:
|
| 66 |
+
tl.static_assert(a.dtype.is_standard_floating(), _builder=_builder)
|
| 67 |
+
tl.static_assert(b.dtype.is_standard_floating(), _builder=_builder)
|
| 68 |
+
a = a.to(in_dtype, _builder=_builder)
|
| 69 |
+
b = b.to(in_dtype, _builder=_builder)
|
| 70 |
+
a = tl.trans(a, _builder=_builder) if trans_a else a
|
| 71 |
+
b = tl.trans(b, _builder=_builder) if trans_b else b
|
| 72 |
+
return tl.dot(
|
| 73 |
+
a, b, allow_tf32=allow_tf32, out_dtype=out_dtype, _builder=_builder
|
| 74 |
+
)
|
| 75 |
+
|
| 76 |
+
return _dot_fn
|
| 77 |
+
|
| 78 |
+
|
| 79 |
+
def is_precision_supported(precision: precision_lib.DotPrecision) -> bool:
|
| 80 |
+
return precision in {
|
| 81 |
+
precision_lib.DotPrecision.F32_F32,
|
| 82 |
+
precision_lib.DotPrecision.TF32_F32,
|
| 83 |
+
precision_lib.DotPrecision.F16_F32,
|
| 84 |
+
precision_lib.DotPrecision.BF16_F32,
|
| 85 |
+
precision_lib.DotPrecision.TF32_F32_3X,
|
| 86 |
+
}
|
| 87 |
+
|
| 88 |
+
|
| 89 |
+
@triton.jit
|
| 90 |
+
def _dot_tf32_f32_3x(a, b, trans_a=False, trans_b=False):
|
| 91 |
+
"""Perform the 3-pass tf32 dot function."""
|
| 92 |
+
tl.static_assert(a.dtype == tl.float32)
|
| 93 |
+
tl.static_assert(b.dtype == tl.float32)
|
| 94 |
+
a_ = (a.to(tl.uint32, bitcast=True) & 0xFFFFE000).to(tl.float32, bitcast=True)
|
| 95 |
+
b_ = (b.to(tl.uint32, bitcast=True) & 0xFFFFE000).to(tl.float32, bitcast=True)
|
| 96 |
+
a_err = a - a_
|
| 97 |
+
b_err = b - b_
|
| 98 |
+
if trans_a:
|
| 99 |
+
a_ = tl.trans(a_)
|
| 100 |
+
a_err = tl.trans(a_err)
|
| 101 |
+
if trans_b:
|
| 102 |
+
b_ = tl.trans(b_)
|
| 103 |
+
b_err = tl.trans(b_err)
|
| 104 |
+
# Add smallest terms first for better accuracy.
|
| 105 |
+
return tl.dot(a_, b_, out_dtype=tl.float32) + (
|
| 106 |
+
tl.dot(a_, b_err, out_dtype=tl.float32)
|
| 107 |
+
+ tl.dot(a_err, b_, out_dtype=tl.float32)
|
| 108 |
+
)
|
| 109 |
+
|
| 110 |
+
|
| 111 |
+
def has_triton_support() -> bool:
|
| 112 |
+
"""Returns True if Triton is supported by the default JAX device."""
|
| 113 |
+
if jax.default_backend() != 'gpu':
|
| 114 |
+
return False
|
| 115 |
+
|
| 116 |
+
# Only currently supported for Ampere and above.
|
| 117 |
+
# return float(jax.devices()[0].compute_capability) >= 8.0
|
| 118 |
+
return True
|
flax_model/alphafold3/jax/gated_linear_unit/block.py
ADDED
|
@@ -0,0 +1,118 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
|
| 2 |
+
|
| 3 |
+
"""Pallas block load / store utilities."""
|
| 4 |
+
|
| 5 |
+
from collections.abc import Sequence
|
| 6 |
+
from typing import Any, TypeAlias
|
| 7 |
+
|
| 8 |
+
from flax_model.alphafold3.jax.common import array_view
|
| 9 |
+
import jax
|
| 10 |
+
import jax.experimental
|
| 11 |
+
from jax.experimental import pallas as pl
|
| 12 |
+
import jax.numpy as jnp
|
| 13 |
+
import jaxtyping
|
| 14 |
+
from jaxtyping import Int # pylint: disable=g-importing-member
|
| 15 |
+
import numpy as np
|
| 16 |
+
import typeguard
|
| 17 |
+
|
| 18 |
+
ArrayT: TypeAlias = Any
|
| 19 |
+
ScalarInt: TypeAlias = (
|
| 20 |
+
Int[ArrayT, ""] | Int[np.generic, ""] | Int[jnp.generic, ""]
|
| 21 |
+
)
|
| 22 |
+
|
| 23 |
+
|
| 24 |
+
@jaxtyping.jaxtyped(typechecker=typeguard.typechecked)
|
| 25 |
+
def load_block(
|
| 26 |
+
ref,
|
| 27 |
+
idx: Sequence[int | ScalarInt],
|
| 28 |
+
*,
|
| 29 |
+
block_shape: Sequence[int | None],
|
| 30 |
+
other=None,
|
| 31 |
+
**kwargs,
|
| 32 |
+
) -> jax.Array:
|
| 33 |
+
"""Loads a block from the given `ref`, masking where necessary."""
|
| 34 |
+
idx, mask = _get_block_indexer_and_mask(ref, idx, block_shape=block_shape)
|
| 35 |
+
if isinstance(ref, array_view.ArrayView):
|
| 36 |
+
idx = ref[idx].offsets
|
| 37 |
+
ref = ref.base
|
| 38 |
+
other = None if mask is None else other
|
| 39 |
+
with jax.experimental.enable_x64():
|
| 40 |
+
return pl.load(ref, idx, mask=mask, other=other, **kwargs)
|
| 41 |
+
|
| 42 |
+
|
| 43 |
+
@jaxtyping.jaxtyped(typechecker=typeguard.typechecked)
|
| 44 |
+
def store_block(
|
| 45 |
+
ref,
|
| 46 |
+
val: jax.Array,
|
| 47 |
+
idx: Sequence[int | ScalarInt],
|
| 48 |
+
*,
|
| 49 |
+
block_shape: Sequence[int | None] | None = None,
|
| 50 |
+
**kwargs,
|
| 51 |
+
):
|
| 52 |
+
"""Stores a block from the given `ref`, masking where necessary."""
|
| 53 |
+
if block_shape is None:
|
| 54 |
+
block_shape = val.shape
|
| 55 |
+
idx, mask = _get_block_indexer_and_mask(ref, idx, block_shape=block_shape)
|
| 56 |
+
if isinstance(ref, array_view.ArrayView):
|
| 57 |
+
idx = ref[idx].offsets
|
| 58 |
+
ref = ref.base
|
| 59 |
+
with jax.experimental.enable_x64():
|
| 60 |
+
pl.store(ref, idx, val.astype(ref.dtype), mask=mask, **kwargs)
|
| 61 |
+
|
| 62 |
+
|
| 63 |
+
def in_bounds_mask(
|
| 64 |
+
idx: Sequence[int | slice | pl.Slice | jax.Array],
|
| 65 |
+
shape: Sequence[int],
|
| 66 |
+
*,
|
| 67 |
+
check: Sequence[bool] | None = None,
|
| 68 |
+
) -> jax.Array | None:
|
| 69 |
+
"""Returns a boolean mask denoting which indices are within bounds.
|
| 70 |
+
|
| 71 |
+
Args:
|
| 72 |
+
idx: Indices for each dimension.
|
| 73 |
+
shape: Shape designating the valid bounds.
|
| 74 |
+
check: Whether or not to check bounds in each dimension. Useful for ignoring
|
| 75 |
+
indices known to be in bounds. Defaults to all True.
|
| 76 |
+
"""
|
| 77 |
+
if check is None:
|
| 78 |
+
check = [True] * len(shape)
|
| 79 |
+
|
| 80 |
+
# Remove `int` indexed dims (mask shape must match slice result shape).
|
| 81 |
+
shape = [dim for i, dim in enumerate(shape) if not isinstance(idx[i], int)]
|
| 82 |
+
check = [chk for i, chk in enumerate(check) if not isinstance(idx[i], int)]
|
| 83 |
+
idx = [idx for idx in idx if not isinstance(idx, int)]
|
| 84 |
+
|
| 85 |
+
mask = None
|
| 86 |
+
for i, (dim_idx, dim, chk) in enumerate(zip(idx, shape, check, strict=True)):
|
| 87 |
+
if not chk:
|
| 88 |
+
continue
|
| 89 |
+
|
| 90 |
+
if isinstance(dim_idx, slice):
|
| 91 |
+
dim_idx = pl.Slice.from_slice(dim_idx, dim)
|
| 92 |
+
if isinstance(dim_idx, pl.Slice):
|
| 93 |
+
dim_idx = dim_idx.start + dim_idx.stride * jnp.arange(dim_idx.size)
|
| 94 |
+
if dim_idx.ndim != 1:
|
| 95 |
+
raise NotImplementedError("Only one-dimensional indices are supported.")
|
| 96 |
+
|
| 97 |
+
bcast_axes = [a for a in range(len(shape)) if a != i]
|
| 98 |
+
dim_mask = jnp.expand_dims(dim_idx < dim, bcast_axes)
|
| 99 |
+
mask = dim_mask if mask is None else (mask & dim_mask)
|
| 100 |
+
return mask
|
| 101 |
+
|
| 102 |
+
|
| 103 |
+
def _get_block_indexer_and_mask(
|
| 104 |
+
ref, idx: Sequence[int | ScalarInt], *, block_shape: Sequence[int | None]
|
| 105 |
+
) -> tuple[tuple[int | slice | pl.Slice, ...], jax.Array | None]:
|
| 106 |
+
"""Return indices and mask for loading / storing a block."""
|
| 107 |
+
shape = ref.shape
|
| 108 |
+
idxs = []
|
| 109 |
+
check = []
|
| 110 |
+
for dim, block_idx, block_dim in zip(shape, idx, block_shape, strict=True):
|
| 111 |
+
if block_dim is None:
|
| 112 |
+
idxs.append(block_idx)
|
| 113 |
+
check.append(False)
|
| 114 |
+
else:
|
| 115 |
+
idxs.append(pl.dslice(block_dim * block_idx, block_dim))
|
| 116 |
+
check.append(dim % block_dim != 0)
|
| 117 |
+
|
| 118 |
+
return tuple(idxs), in_bounds_mask(idxs, shape, check=check)
|
flax_model/alphafold3/jax/gated_linear_unit/gated_linear_unit.py
ADDED
|
@@ -0,0 +1,116 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
|
| 2 |
+
|
| 3 |
+
"""Public API for gated linear unit functions."""
|
| 4 |
+
|
| 5 |
+
from collections.abc import Callable
|
| 6 |
+
import typing
|
| 7 |
+
from typing import Literal, TypeAlias
|
| 8 |
+
|
| 9 |
+
from flax_model.alphafold3.jax.common import array_view
|
| 10 |
+
from flax_model.alphafold3.jax.common import triton_utils
|
| 11 |
+
from flax_model.alphafold3.jax.gated_linear_unit import gated_linear_unit_base
|
| 12 |
+
from flax_model.alphafold3.jax.gated_linear_unit import matmul_ext
|
| 13 |
+
import jax
|
| 14 |
+
import jaxtyping
|
| 15 |
+
from jaxtyping import Array, Float # pylint: disable=g-importing-member,g-multiple-import
|
| 16 |
+
import typeguard
|
| 17 |
+
|
| 18 |
+
Implementation: TypeAlias = Literal['xla', 'triton']
|
| 19 |
+
|
| 20 |
+
|
| 21 |
+
class PallasGatedLinearUnit(gated_linear_unit_base.GatedLinearUnit):
|
| 22 |
+
"""Pallas gated linear unit."""
|
| 23 |
+
|
| 24 |
+
def _fwd(self, x, weight, *, activation, precision):
|
| 25 |
+
weight_view = array_view.ArrayView(weight)
|
| 26 |
+
return self.apply_vmap_rule_forward(
|
| 27 |
+
matmul_ext.gated_linear_unit,
|
| 28 |
+
activation=activation,
|
| 29 |
+
precision=precision,
|
| 30 |
+
)(
|
| 31 |
+
x,
|
| 32 |
+
weight_view[:, 1],
|
| 33 |
+
weight_view[:, 0],
|
| 34 |
+
)
|
| 35 |
+
|
| 36 |
+
|
| 37 |
+
@jaxtyping.jaxtyped(typechecker=typeguard.typechecked)
|
| 38 |
+
def gated_linear_unit(
|
| 39 |
+
x: Float[Array, '*B M K'],
|
| 40 |
+
weight: Float[Array, 'K 2 N'],
|
| 41 |
+
*,
|
| 42 |
+
activation: Callable[[jax.Array], jax.Array] | None = None,
|
| 43 |
+
precision: jax.lax.Precision | None = None,
|
| 44 |
+
implementation: Implementation | None = None,
|
| 45 |
+
) -> Float[Array, '*B M N']:
|
| 46 |
+
"""Applies a gated linear unit (https://arxiv.org/abs/1612.08083).
|
| 47 |
+
|
| 48 |
+
Computes `activation(x @ weight[:, 0]) * x @ weight[:, 1]`.
|
| 49 |
+
|
| 50 |
+
This is SwiGLU when `activation=jax.nn.swish`, GEGLU when
|
| 51 |
+
`activation=jax.nn.gelu`, REGLU when `activation=jax.nn.relu`, and GLU when
|
| 52 |
+
`activation=jax.nn.sigmoid` (https://arxiv.org/abs/2002.05202).
|
| 53 |
+
|
| 54 |
+
Args:
|
| 55 |
+
x: the input array.
|
| 56 |
+
weight: the combined weight array.
|
| 57 |
+
activation: optional activation function.
|
| 58 |
+
precision: specifies the matrix multiplication precision. Either `None`
|
| 59 |
+
(default), which means the default precision for the backend, or a
|
| 60 |
+
`jax.lax.Precision` enum.
|
| 61 |
+
implementation: if `None` (default), an implementation is automatically
|
| 62 |
+
chosen. 'xla' will use standard XLA and work on any platform, and 'triton'
|
| 63 |
+
will use a fused Triton GPU kernel. Only a subset of data types, shapes
|
| 64 |
+
and GPUs are supported by 'triton', with an exception thrown in this case.
|
| 65 |
+
|
| 66 |
+
Raises:
|
| 67 |
+
NotImplementedError: if `implementation='triton'` does not support a given
|
| 68 |
+
input or device.
|
| 69 |
+
ValueError: if the arguments are invalid.
|
| 70 |
+
|
| 71 |
+
Returns:
|
| 72 |
+
The output array.
|
| 73 |
+
"""
|
| 74 |
+
|
| 75 |
+
match implementation:
|
| 76 |
+
case 'triton':
|
| 77 |
+
if not triton_utils.has_triton_support():
|
| 78 |
+
raise NotImplementedError('Triton not supported on this platform.')
|
| 79 |
+
case _:
|
| 80 |
+
...
|
| 81 |
+
|
| 82 |
+
if x.dtype.name != weight.dtype.name:
|
| 83 |
+
raise ValueError(
|
| 84 |
+
f'Input and weight must have the same dtype. {x.dtype} !='
|
| 85 |
+
f' {weight.dtype}'
|
| 86 |
+
)
|
| 87 |
+
|
| 88 |
+
if implementation is not None:
|
| 89 |
+
named_args = typing.get_args(Implementation)
|
| 90 |
+
if implementation not in named_args:
|
| 91 |
+
raise ValueError(
|
| 92 |
+
f'Unsupported named implementation. Must be one of {named_args}.'
|
| 93 |
+
)
|
| 94 |
+
|
| 95 |
+
# if implementation is None or implementation == 'triton':
|
| 96 |
+
# try:
|
| 97 |
+
# return PallasGatedLinearUnit()(
|
| 98 |
+
# x=x,
|
| 99 |
+
# weight=weight,
|
| 100 |
+
# activation=activation,
|
| 101 |
+
# precision=precision,
|
| 102 |
+
# )
|
| 103 |
+
# # When `implementation=None`, we must catch any exception, and use XLA
|
| 104 |
+
# # as a fallback. As we rely on a third-party library (Triton), it might
|
| 105 |
+
# # not be possible to enumerate all possible exceptions that could be
|
| 106 |
+
# # thrown, hence catching the broadest possible one.
|
| 107 |
+
# except Exception as e: # pylint: disable=broad-exception-caught
|
| 108 |
+
# if implementation == 'triton':
|
| 109 |
+
# raise e
|
| 110 |
+
|
| 111 |
+
return gated_linear_unit_base.gated_linear_unit_xla(
|
| 112 |
+
x=x,
|
| 113 |
+
weight=weight,
|
| 114 |
+
activation=activation,
|
| 115 |
+
precision=precision,
|
| 116 |
+
)
|
flax_model/alphafold3/jax/gated_linear_unit/matmul_config.py
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
|
| 2 |
+
|
| 3 |
+
"""Auto-tuned configs for matmul."""
|
| 4 |
+
|
| 5 |
+
import dataclasses
|
| 6 |
+
import functools
|
| 7 |
+
import math
|
| 8 |
+
|
| 9 |
+
import jax
|
| 10 |
+
from jax.experimental import pallas as pl
|
| 11 |
+
|
| 12 |
+
|
| 13 |
+
@dataclasses.dataclass(frozen=True, kw_only=True)
|
| 14 |
+
class Config:
|
| 15 |
+
block_m: int
|
| 16 |
+
block_n: int
|
| 17 |
+
block_k: int
|
| 18 |
+
num_warps: int
|
| 19 |
+
num_stages: int
|
| 20 |
+
|
| 21 |
+
|
| 22 |
+
@functools.cache
|
| 23 |
+
def _get_best_block_size(
|
| 24 |
+
m: int, n: int, k: int, core_count: int
|
| 25 |
+
) -> tuple[int, int, int]:
|
| 26 |
+
"""Returns the best block size for the given shape."""
|
| 27 |
+
min_block_dim = 32
|
| 28 |
+
block_m = min(max(min_block_dim, pl.next_power_of_2(m)), 128)
|
| 29 |
+
block_n = min(max(min_block_dim, pl.next_power_of_2(n)), 256)
|
| 30 |
+
block_n = min(block_n, (128 * 128) // block_m)
|
| 31 |
+
block_k = 32
|
| 32 |
+
split_k = 1
|
| 33 |
+
num_blocks = pl.cdiv(m, block_m) * pl.cdiv(n, block_n)
|
| 34 |
+
while num_blocks < core_count:
|
| 35 |
+
if block_m > min_block_dim:
|
| 36 |
+
block_m //= 2
|
| 37 |
+
num_blocks = pl.cdiv(m, block_m) * pl.cdiv(n, block_n)
|
| 38 |
+
elif split_k * block_k < pl.next_power_of_2(k):
|
| 39 |
+
split_k *= 2
|
| 40 |
+
num_blocks *= 2
|
| 41 |
+
else:
|
| 42 |
+
break
|
| 43 |
+
return block_m, block_n, block_k
|
| 44 |
+
|
| 45 |
+
|
| 46 |
+
def _abstractify(x):
|
| 47 |
+
return jax.api_util.shaped_abstractify(x) if isinstance(x, jax.Array) else x
|
| 48 |
+
|
| 49 |
+
|
| 50 |
+
def get_config(
|
| 51 |
+
x: jax.Array, w: jax.Array, core_count: int | None = None
|
| 52 |
+
) -> Config:
|
| 53 |
+
"""Returns a config for the given args."""
|
| 54 |
+
if core_count is None:
|
| 55 |
+
core_count = jax.devices()[0].core_count
|
| 56 |
+
x = _abstractify(x)
|
| 57 |
+
w = _abstractify(w)
|
| 58 |
+
m, k = math.prod(x.shape[:-1]), x.shape[-1]
|
| 59 |
+
n = w.shape[1]
|
| 60 |
+
if n >= m: # Prefer `block_n` > `block_m`.
|
| 61 |
+
block_m, block_n, block_k = _get_best_block_size(m, n, k, core_count)
|
| 62 |
+
else:
|
| 63 |
+
block_n, block_m, block_k = _get_best_block_size(n, m, k, core_count)
|
| 64 |
+
return Config(
|
| 65 |
+
block_m=block_m,
|
| 66 |
+
block_n=block_n // 2, # Halve `block_n` as we read two `w` blocks.
|
| 67 |
+
block_k=block_k,
|
| 68 |
+
num_warps=4,
|
| 69 |
+
num_stages=4,
|
| 70 |
+
)
|
flax_model/alphafold3/jax/geometry/__init__.py
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
|
| 2 |
+
|
| 3 |
+
"""Geometry Module."""
|
| 4 |
+
|
| 5 |
+
from flax_model.alphafold3.jax.geometry import rigid_matrix_vector
|
| 6 |
+
from flax_model.alphafold3.jax.geometry import rotation_matrix
|
| 7 |
+
from flax_model.alphafold3.jax.geometry import struct_of_array
|
| 8 |
+
from flax_model.alphafold3.jax.geometry import vector
|
| 9 |
+
|
| 10 |
+
Rot3Array = rotation_matrix.Rot3Array
|
| 11 |
+
Rigid3Array = rigid_matrix_vector.Rigid3Array
|
| 12 |
+
|
| 13 |
+
StructOfArray = struct_of_array.StructOfArray
|
| 14 |
+
|
| 15 |
+
Vec3Array = vector.Vec3Array
|
| 16 |
+
square_euclidean_distance = vector.square_euclidean_distance
|
| 17 |
+
euclidean_distance = vector.euclidean_distance
|
| 18 |
+
dihedral_angle = vector.dihedral_angle
|
| 19 |
+
dot = vector.dot
|
| 20 |
+
cross = vector.cross
|
flax_model/alphafold3/test_data/miniature_databases/nt_rna_2023_02_23_clust_seq_id_90_cov_80_rep_seq__subsampled_1000.fasta
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
scripts/infer_jackhmmer.sh
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/bin/bash
|
| 2 |
+
set -euo pipefail
|
| 3 |
+
|
| 4 |
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
| 5 |
+
PROJECT_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)"
|
| 6 |
+
|
| 7 |
+
|
| 8 |
+
export PATH="${PROJECT_ROOT}/flax_model/alphafold3/_tools/hmmer/bin:${PATH}"
|
| 9 |
+
export TF_CPP_MIN_LOG_LEVEL="${TF_CPP_MIN_LOG_LEVEL:-0}"
|
| 10 |
+
export JAX_TRACEBACK_FILTERING="${JAX_TRACEBACK_FILTERING:-off}"
|
| 11 |
+
export XLA_CLIENT_MEM_FRACTION="${XLA_CLIENT_MEM_FRACTION:-0.95}"
|
| 12 |
+
export TRITON_ENABLE_GLOBAL_TO_LOCAL="${TRITON_ENABLE_GLOBAL_TO_LOCAL:-1}"
|
| 13 |
+
export TRITON_USE_MAKE_BLOCK_PTR="${TRITON_USE_MAKE_BLOCK_PTR:-1}"
|
| 14 |
+
export TRITON_DEFAULT_ENABLE_NUM_VGPRS512="${TRITON_DEFAULT_ENABLE_NUM_VGPRS512:-1}"
|
| 15 |
+
export HIP_VISIBLE_DEVICES="${HIP_VISIBLE_DEVICES:-0}"
|
| 16 |
+
|
| 17 |
+
MODEL_ROOT="${ONESCIENCE_MODELS_DIR:-${PROJECT_ROOT}/weight}"
|
| 18 |
+
MODEL_DIR="${ALPHAFOLD3_MODEL_DIR:-${MODEL_ROOT}}"
|
| 19 |
+
DATASET_ROOT="${ALPHAFOLD3_DATASET_ROOT:-${ONESCIENCE_DATASETS_DIR:-${PROJECT_ROOT}/data}/alphafold3}"
|
| 20 |
+
DB_DIRS="${ALPHAFOLD3_DB_DIR:-${DATASET_ROOT}/public_databases}"
|
| 21 |
+
MMSEQS_DB_DIRS="${ALPHAFOLD3_MMSEQS_DB_DIR:-${DATASET_ROOT}/mmseqsDB}"
|
| 22 |
+
JSON_PATH="${ALPHAFOLD3_JSON_PATH:-${PROJECT_ROOT}/inputs/t1119_search.json}"
|
| 23 |
+
OUTPUT_DIR="${ALPHAFOLD3_OUTPUT_DIR:-${PROJECT_ROOT}/outputs}"
|
| 24 |
+
RUN_INFERENCE="${ALPHAFOLD3_RUN_INFERENCE:-false}"
|
| 25 |
+
FLASH_ATTENTION="${ALPHAFOLD3_FLASH_ATTENTION:-cutlass}"
|
| 26 |
+
|
| 27 |
+
mkdir -p "${OUTPUT_DIR}"
|
| 28 |
+
|
| 29 |
+
python "${PROJECT_ROOT}/scripts/run_alphafold.py" \
|
| 30 |
+
--json_path="${JSON_PATH}" \
|
| 31 |
+
--model_dir="${MODEL_DIR}" \
|
| 32 |
+
--output_dir="${OUTPUT_DIR}" \
|
| 33 |
+
--run_data_pipeline=true \
|
| 34 |
+
--run_inference="${RUN_INFERENCE}" \
|
| 35 |
+
--flash_attention_implementation="${FLASH_ATTENTION}" \
|
| 36 |
+
--use_mmseqs=false \
|
| 37 |
+
--use_mmseqs_gpu=false \
|
| 38 |
+
--db_dir="${DB_DIRS}" \
|
| 39 |
+
--mmseqs_db_dir="${MMSEQS_DB_DIRS}" \
|
| 40 |
+
--small_bfd_database_path="${DATASET_ROOT}/jackhmmer_split/bfd-first_non_consensus_sequences.fasta@64" \
|
| 41 |
+
--small_bfd_z_value=65928866 \
|
| 42 |
+
--mgnify_database_path="${DATASET_ROOT}/jackhmmer_split/mgy_clusters_2022_05.fa@512" \
|
| 43 |
+
--mgnify_z_value=623796864 \
|
| 44 |
+
--uniprot_cluster_annot_database_path="${DATASET_ROOT}/jackhmmer_split/uniprot_cluster_annot_2021_04.fa@256" \
|
| 45 |
+
--uniprot_cluster_annot_z_value=225619586 \
|
| 46 |
+
--uniref90_database_path="${DATASET_ROOT}/jackhmmer_split/uniref90_2022_05.fa@128" \
|
| 47 |
+
--uniref90_z_value=153742194 \
|
| 48 |
+
--jackhmmer_n_cpu="${ALPHAFOLD3_JACKHMMER_N_CPU:-2}" \
|
| 49 |
+
--jackhmmer_max_parallel_shards="${ALPHAFOLD3_JACKHMMER_MAX_PARALLEL_SHARDS:-1}" \
|
| 50 |
+
--jackhmmer_max_threads="${ALPHAFOLD3_JACKHMMER_MAX_THREADS:-8}" \
|
| 51 |
+
--nhmmer_n_cpu="${ALPHAFOLD3_NHMMER_N_CPU:-2}" \
|
| 52 |
+
--nhmmer_max_parallel_shards="${ALPHAFOLD3_NHMMER_MAX_PARALLEL_SHARDS:-1}" \
|
| 53 |
+
--nhmmer_max_threads="${ALPHAFOLD3_NHMMER_MAX_THREADS:-8}"
|
scripts/preflight.py
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python3
|
| 2 |
+
from __future__ import annotations
|
| 3 |
+
|
| 4 |
+
import argparse
|
| 5 |
+
import hashlib
|
| 6 |
+
import json
|
| 7 |
+
from pathlib import Path
|
| 8 |
+
|
| 9 |
+
try:
|
| 10 |
+
import yaml
|
| 11 |
+
except ImportError as exc:
|
| 12 |
+
raise SystemExit("PyYAML is required for preflight: pip install pyyaml") from exc
|
| 13 |
+
|
| 14 |
+
|
| 15 |
+
MODEL_FILES = {
|
| 16 |
+
"af3.bin": (1146811260, "a43670ea1fae790cd30ccc8c8bf836c4098014c45250fb310a5338407e321de7"),
|
| 17 |
+
"libflash_atten_c.so": (75882104, "2ea956e8d13317dd795ffce846079f20e502a39df66802f485d5f25900ef493b"),
|
| 18 |
+
"mmseqs/bin/mmseqs": (14417192, "d166c67ca79089ea5f89ec1133f67ab1c216e1734a0408bbc7205e588fe865ed"),
|
| 19 |
+
"mmseqs/lib/libmarv.so": (58277568, "fdc787467b6d05d36db1f034a2cb8944b5b072bbe6d46172013810a066583928"),
|
| 20 |
+
}
|
| 21 |
+
|
| 22 |
+
DATASET_REQUIRED = [
|
| 23 |
+
"infer_input_data/readme.md",
|
| 24 |
+
"infer_input_data/all_data/7r6r_data.json",
|
| 25 |
+
"infer_input_data/all_data/t1119_data.json",
|
| 26 |
+
"public_databases/pdb_seqres_2022_09_28.fasta",
|
| 27 |
+
"mmseqsDB/small_bfd_db",
|
| 28 |
+
"mmseqsDB/small_bfd_db.dbtype",
|
| 29 |
+
"jackhmmer_split/bfd-first_non_consensus_sequences.fasta-00000-of-00064",
|
| 30 |
+
]
|
| 31 |
+
|
| 32 |
+
|
| 33 |
+
def sha256(path: Path) -> str:
|
| 34 |
+
h = hashlib.sha256()
|
| 35 |
+
with path.open("rb") as f:
|
| 36 |
+
for chunk in iter(lambda: f.read(1024 * 1024), b""):
|
| 37 |
+
h.update(chunk)
|
| 38 |
+
return h.hexdigest()
|
| 39 |
+
|
| 40 |
+
|
| 41 |
+
def check_json(path: Path) -> None:
|
| 42 |
+
data = json.loads(path.read_text(encoding="utf-8"))
|
| 43 |
+
if data.get("dialect") != "alphafold3":
|
| 44 |
+
raise AssertionError(f"{path} dialect is not alphafold3")
|
| 45 |
+
if not data.get("name"):
|
| 46 |
+
raise AssertionError(f"{path} missing name")
|
| 47 |
+
if not isinstance(data.get("sequences"), list) or not data["sequences"]:
|
| 48 |
+
raise AssertionError(f"{path} missing sequences")
|
| 49 |
+
|
| 50 |
+
|
| 51 |
+
def main() -> None:
|
| 52 |
+
parser = argparse.ArgumentParser()
|
| 53 |
+
parser.add_argument("--root", default=".")
|
| 54 |
+
parser.add_argument("--dataset-root", default="data/alphafold3_dataset")
|
| 55 |
+
parser.add_argument("--model-dir", default="checkpoints/AlphaFold3")
|
| 56 |
+
parser.add_argument("--skip-hash", action="store_true")
|
| 57 |
+
args = parser.parse_args()
|
| 58 |
+
|
| 59 |
+
root = Path(args.root).resolve()
|
| 60 |
+
model_dir = (root / args.model_dir).resolve()
|
| 61 |
+
dataset_root = (root / args.dataset_root).resolve()
|
| 62 |
+
|
| 63 |
+
config = yaml.safe_load((root / "conf" / "alphafold3_paths.yaml").read_text(encoding="utf-8"))
|
| 64 |
+
if config["model"]["repo_id"] != "OneScience/AlphaFold3/":
|
| 65 |
+
raise AssertionError("model repo_id mismatch in conf/alphafold3_paths.yaml")
|
| 66 |
+
if config["dataset"]["repo_id"] != "OneScience/AlphaFold3_dataset":
|
| 67 |
+
raise AssertionError("dataset repo_id mismatch in conf/alphafold3_paths.yaml")
|
| 68 |
+
|
| 69 |
+
for rel, (size, digest) in MODEL_FILES.items():
|
| 70 |
+
path = model_dir / rel
|
| 71 |
+
if not path.is_file():
|
| 72 |
+
raise AssertionError(f"missing model file: {path}")
|
| 73 |
+
if path.stat().st_size != size:
|
| 74 |
+
raise AssertionError(f"model size mismatch: {path}")
|
| 75 |
+
if not args.skip_hash and sha256(path) != digest:
|
| 76 |
+
raise AssertionError(f"model sha256 mismatch: {path}")
|
| 77 |
+
|
| 78 |
+
for rel in DATASET_REQUIRED:
|
| 79 |
+
path = dataset_root / rel
|
| 80 |
+
if not path.exists():
|
| 81 |
+
raise AssertionError(f"missing dataset dependency: {path}")
|
| 82 |
+
|
| 83 |
+
check_json(root / "inputs" / "7r6r_data.json")
|
| 84 |
+
check_json(root / "inputs" / "t1119_search.json")
|
| 85 |
+
print("model_preflight_ok: true")
|
| 86 |
+
|
| 87 |
+
|
| 88 |
+
if __name__ == "__main__":
|
| 89 |
+
main()
|
scripts/run_alphafold.py
ADDED
|
@@ -0,0 +1,1194 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
|
| 2 |
+
|
| 3 |
+
"""AlphaFold 3 structure prediction script.
|
| 4 |
+
|
| 5 |
+
AlphaFold 3 source code is licensed under CC BY-NC-SA 4.0. To view a copy of
|
| 6 |
+
this license, visit https://creativecommons.org/licenses/by-nc-sa/4.0/
|
| 7 |
+
|
| 8 |
+
To request access to the AlphaFold 3 model parameters, follow the process set
|
| 9 |
+
out at https://github.com/google-deepmind/alphafold3. You may only use these
|
| 10 |
+
if received directly from Google. Use is subject to terms of use available at
|
| 11 |
+
https://github.com/google-deepmind/alphafold3/blob/main/WEIGHTS_TERMS_OF_USE.md
|
| 12 |
+
"""
|
| 13 |
+
|
| 14 |
+
from collections.abc import Callable, Sequence
|
| 15 |
+
import csv
|
| 16 |
+
import dataclasses
|
| 17 |
+
import datetime
|
| 18 |
+
import functools
|
| 19 |
+
import multiprocessing
|
| 20 |
+
import os
|
| 21 |
+
import pathlib
|
| 22 |
+
import shutil
|
| 23 |
+
import string
|
| 24 |
+
import sys
|
| 25 |
+
import textwrap
|
| 26 |
+
import time
|
| 27 |
+
import typing
|
| 28 |
+
from typing import overload
|
| 29 |
+
|
| 30 |
+
_PROJECT_ROOT = pathlib.Path(__file__).resolve().parents[1]
|
| 31 |
+
if str(_PROJECT_ROOT) not in sys.path:
|
| 32 |
+
sys.path.insert(0, str(_PROJECT_ROOT))
|
| 33 |
+
|
| 34 |
+
from absl import app
|
| 35 |
+
from absl import flags
|
| 36 |
+
from flax_model.alphafold3.common import folding_input
|
| 37 |
+
from flax_model.alphafold3.common import resources
|
| 38 |
+
from flax_model.alphafold3.constants import chemical_components
|
| 39 |
+
import flax_model.alphafold3.cpp as af3_cpp
|
| 40 |
+
from flax_model.alphafold3.data import featurisation
|
| 41 |
+
from flax_model.alphafold3.data import pipeline
|
| 42 |
+
from flax_model.alphafold3.jax.attention import attention
|
| 43 |
+
from flax_model.alphafold3.model import features
|
| 44 |
+
from flax_model.alphafold3.model import model
|
| 45 |
+
from flax_model.alphafold3.model import params
|
| 46 |
+
from flax_model.alphafold3.model import post_processing
|
| 47 |
+
from flax_model.alphafold3.model.components import utils
|
| 48 |
+
import haiku as hk
|
| 49 |
+
import jax
|
| 50 |
+
from jax import numpy as jnp
|
| 51 |
+
import numpy as np
|
| 52 |
+
|
| 53 |
+
|
| 54 |
+
_HOME_DIR = pathlib.Path(os.environ.get('HOME', _PROJECT_ROOT))
|
| 55 |
+
_MODELS_ROOT = pathlib.Path(
|
| 56 |
+
os.environ.get('ONESCIENCE_MODELS_DIR', _PROJECT_ROOT / 'weight')
|
| 57 |
+
)
|
| 58 |
+
_DATASETS_ROOT = pathlib.Path(
|
| 59 |
+
os.environ.get('ONESCIENCE_DATASETS_DIR', _HOME_DIR)
|
| 60 |
+
) / 'alphafold3'
|
| 61 |
+
_DEFAULT_MODEL_DIR = pathlib.Path(
|
| 62 |
+
os.environ.get('ALPHAFOLD3_MODEL_DIR', _MODELS_ROOT / 'AlphaFold3')
|
| 63 |
+
)
|
| 64 |
+
_DEFAULT_DB_DIR = pathlib.Path(
|
| 65 |
+
os.environ.get('ALPHAFOLD3_DB_DIR', _DATASETS_ROOT / 'public_databases')
|
| 66 |
+
)
|
| 67 |
+
_DEFAULT_MMSEQS_DB_DIR = pathlib.Path(
|
| 68 |
+
os.environ.get('ALPHAFOLD3_MMSEQS_DB_DIR', _DATASETS_ROOT / 'mmseqsDB')
|
| 69 |
+
)
|
| 70 |
+
|
| 71 |
+
# Input and output paths.
|
| 72 |
+
_JSON_PATH = flags.DEFINE_string(
|
| 73 |
+
'json_path',
|
| 74 |
+
None,
|
| 75 |
+
'Path to the input JSON file.',
|
| 76 |
+
)
|
| 77 |
+
_INPUT_DIR = flags.DEFINE_string(
|
| 78 |
+
'input_dir',
|
| 79 |
+
None,
|
| 80 |
+
'Path to the directory containing input JSON files.',
|
| 81 |
+
)
|
| 82 |
+
_OUTPUT_DIR = flags.DEFINE_string(
|
| 83 |
+
'output_dir',
|
| 84 |
+
None,
|
| 85 |
+
'Path to a directory where the results will be saved.',
|
| 86 |
+
)
|
| 87 |
+
MODEL_DIR = flags.DEFINE_string(
|
| 88 |
+
'model_dir',
|
| 89 |
+
_DEFAULT_MODEL_DIR.as_posix(),
|
| 90 |
+
'Path to the model to use for inference.',
|
| 91 |
+
)
|
| 92 |
+
|
| 93 |
+
# Control which stages to run.
|
| 94 |
+
_RUN_DATA_PIPELINE = flags.DEFINE_bool(
|
| 95 |
+
'run_data_pipeline',
|
| 96 |
+
True,
|
| 97 |
+
'Whether to run the data pipeline on the fold inputs.',
|
| 98 |
+
)
|
| 99 |
+
_RUN_INFERENCE = flags.DEFINE_bool(
|
| 100 |
+
'run_inference',
|
| 101 |
+
True,
|
| 102 |
+
'Whether to run inference on the fold inputs.',
|
| 103 |
+
)
|
| 104 |
+
|
| 105 |
+
|
| 106 |
+
_DEFAULT_MMSEQS_OPTIONS ='--num-iterations 1 --db-load-mode 2 -a --max-seqs 10000 --prefilter-mode 1'
|
| 107 |
+
_DEFAULT_R2MSA_OPTIONS ='--filter-msa 1 --filter-min-enable 1000 --diff 3000 --qid 0.0,0.2,0.4,0.6,0.8,1.0 --qsc 0 --max-seq-id 0.95'
|
| 108 |
+
_AF3_DIR = _PROJECT_ROOT / 'flax_model' / 'alphafold3'
|
| 109 |
+
_HMMER_BIN_DIR = _AF3_DIR / '_tools' / 'hmmer' / 'bin'
|
| 110 |
+
|
| 111 |
+
|
| 112 |
+
def _resolve_af3_tool(binary_name: str) -> str | None:
|
| 113 |
+
local_binary = _HMMER_BIN_DIR / binary_name
|
| 114 |
+
if local_binary.exists():
|
| 115 |
+
return str(local_binary)
|
| 116 |
+
return shutil.which(binary_name)
|
| 117 |
+
|
| 118 |
+
|
| 119 |
+
def _available_cpu_count() -> int:
|
| 120 |
+
if hasattr(os, 'sched_getaffinity'):
|
| 121 |
+
return len(os.sched_getaffinity(0))
|
| 122 |
+
return multiprocessing.cpu_count()
|
| 123 |
+
|
| 124 |
+
|
| 125 |
+
_USE_MMSEQS = flags.DEFINE_bool(
|
| 126 |
+
'use_mmseqs',
|
| 127 |
+
False,
|
| 128 |
+
'Whether to use mmseqs for protein MSA search',
|
| 129 |
+
)
|
| 130 |
+
_USE_MMSEQS_GPU = flags.DEFINE_bool(
|
| 131 |
+
'use_mmseqs_gpu',
|
| 132 |
+
False,
|
| 133 |
+
'Whether to use mmseqs GPU for protein MSA search',
|
| 134 |
+
)
|
| 135 |
+
_MMSEQS_OPTIONS = flags.DEFINE_string(
|
| 136 |
+
'mmseqs_options',
|
| 137 |
+
_DEFAULT_MMSEQS_OPTIONS,
|
| 138 |
+
'mmseqs serach options',
|
| 139 |
+
)
|
| 140 |
+
_R2MSA_OPTIONS = flags.DEFINE_string(
|
| 141 |
+
'result2msa_options',
|
| 142 |
+
_DEFAULT_R2MSA_OPTIONS,
|
| 143 |
+
'mmseqs result2msa options',
|
| 144 |
+
)
|
| 145 |
+
|
| 146 |
+
|
| 147 |
+
# Binary paths.
|
| 148 |
+
_JACKHMMER_BINARY_PATH = flags.DEFINE_string(
|
| 149 |
+
'jackhmmer_binary_path',
|
| 150 |
+
_resolve_af3_tool('jackhmmer'),
|
| 151 |
+
'Path to the Jackhmmer binary.',
|
| 152 |
+
)
|
| 153 |
+
_NHMMER_BINARY_PATH = flags.DEFINE_string(
|
| 154 |
+
'nhmmer_binary_path',
|
| 155 |
+
_resolve_af3_tool('nhmmer'),
|
| 156 |
+
'Path to the Nhmmer binary.',
|
| 157 |
+
)
|
| 158 |
+
_HMMALIGN_BINARY_PATH = flags.DEFINE_string(
|
| 159 |
+
'hmmalign_binary_path',
|
| 160 |
+
_resolve_af3_tool('hmmalign'),
|
| 161 |
+
'Path to the Hmmalign binary.',
|
| 162 |
+
)
|
| 163 |
+
_HMMSEARCH_BINARY_PATH = flags.DEFINE_string(
|
| 164 |
+
'hmmsearch_binary_path',
|
| 165 |
+
_resolve_af3_tool('hmmsearch'),
|
| 166 |
+
'Path to the Hmmsearch binary.',
|
| 167 |
+
)
|
| 168 |
+
_HMMBUILD_BINARY_PATH = flags.DEFINE_string(
|
| 169 |
+
'hmmbuild_binary_path',
|
| 170 |
+
_resolve_af3_tool('hmmbuild'),
|
| 171 |
+
'Path to the Hmmbuild binary.',
|
| 172 |
+
)
|
| 173 |
+
_MMSEQS_BINARY_PATH = flags.DEFINE_string(
|
| 174 |
+
'mmseqs_binary_path',
|
| 175 |
+
shutil.which('mmseqs'),
|
| 176 |
+
'Path to the mmseqs binary.',
|
| 177 |
+
)
|
| 178 |
+
|
| 179 |
+
# Database paths.
|
| 180 |
+
DB_DIR = flags.DEFINE_multi_string(
|
| 181 |
+
'db_dir',
|
| 182 |
+
(_DEFAULT_DB_DIR.as_posix(),),
|
| 183 |
+
'Path to the directory containing the databases. Can be specified multiple'
|
| 184 |
+
' times to search multiple directories in order.',
|
| 185 |
+
)
|
| 186 |
+
MMSEQS_DB_DIR = flags.DEFINE_multi_string(
|
| 187 |
+
'mmseqs_db_dir',
|
| 188 |
+
(_DEFAULT_MMSEQS_DB_DIR.as_posix(),),
|
| 189 |
+
'Path to the directory containing the mmseqs databases. Can be specified multiple'
|
| 190 |
+
' times to search multiple directories in order.',
|
| 191 |
+
)
|
| 192 |
+
|
| 193 |
+
_SMALL_BFD_DATABASE_PATH = flags.DEFINE_string(
|
| 194 |
+
'small_bfd_database_path',
|
| 195 |
+
'${DB_DIR}/bfd-first_non_consensus_sequences.fasta',
|
| 196 |
+
'Small BFD database path, used for protein MSA search.',
|
| 197 |
+
)
|
| 198 |
+
_SMALL_BFD_Z_VALUE = flags.DEFINE_integer(
|
| 199 |
+
'small_bfd_z_value',
|
| 200 |
+
None,
|
| 201 |
+
'The Z-value representing the database size in number of sequences for'
|
| 202 |
+
' E-value calculation. Must be set for sharded databases.',
|
| 203 |
+
lower_bound=0,
|
| 204 |
+
)
|
| 205 |
+
_MGNIFY_DATABASE_PATH = flags.DEFINE_string(
|
| 206 |
+
'mgnify_database_path',
|
| 207 |
+
'${DB_DIR}/mgy_clusters_2022_05.fa',
|
| 208 |
+
'Mgnify database path, used for protein MSA search.',
|
| 209 |
+
)
|
| 210 |
+
_MGNIFY_Z_VALUE = flags.DEFINE_integer(
|
| 211 |
+
'mgnify_z_value',
|
| 212 |
+
None,
|
| 213 |
+
'The Z-value representing the database size in number of sequences for'
|
| 214 |
+
' E-value calculation. Must be set for sharded databases.',
|
| 215 |
+
lower_bound=0,
|
| 216 |
+
)
|
| 217 |
+
_UNIPROT_CLUSTER_ANNOT_DATABASE_PATH = flags.DEFINE_string(
|
| 218 |
+
'uniprot_cluster_annot_database_path',
|
| 219 |
+
'${DB_DIR}/uniprot_all_2021_04.fa',
|
| 220 |
+
'UniProt database path, used for protein paired MSA search.',
|
| 221 |
+
)
|
| 222 |
+
_UNIPROT_CLUSTER_ANNOT_Z_VALUE = flags.DEFINE_integer(
|
| 223 |
+
'uniprot_cluster_annot_z_value',
|
| 224 |
+
None,
|
| 225 |
+
'The Z-value representing the database size in number of sequences for'
|
| 226 |
+
' E-value calculation. Must be set for sharded databases.',
|
| 227 |
+
lower_bound=0,
|
| 228 |
+
)
|
| 229 |
+
_UNIREF90_DATABASE_PATH = flags.DEFINE_string(
|
| 230 |
+
'uniref90_database_path',
|
| 231 |
+
'${DB_DIR}/uniref90_2022_05.fa',
|
| 232 |
+
'UniRef90 database path, used for MSA search. The MSA obtained by '
|
| 233 |
+
'searching it is used to construct the profile for template search.',
|
| 234 |
+
)
|
| 235 |
+
_UNIREF90_Z_VALUE = flags.DEFINE_integer(
|
| 236 |
+
'uniref90_z_value',
|
| 237 |
+
None,
|
| 238 |
+
'The Z-value representing the database size in number of sequences for'
|
| 239 |
+
' E-value calculation. Must be set for sharded databases.',
|
| 240 |
+
lower_bound=0,
|
| 241 |
+
)
|
| 242 |
+
_NTRNA_DATABASE_PATH = flags.DEFINE_string(
|
| 243 |
+
'ntrna_database_path',
|
| 244 |
+
'${DB_DIR}/nt_rna_2023_02_23_clust_seq_id_90_cov_80_rep_seq.fasta',
|
| 245 |
+
'NT-RNA database path, used for RNA MSA search.',
|
| 246 |
+
)
|
| 247 |
+
_NTRNA_Z_VALUE = flags.DEFINE_float(
|
| 248 |
+
'ntrna_z_value',
|
| 249 |
+
None,
|
| 250 |
+
'The Z-value representing the database size in megabases for E-value'
|
| 251 |
+
' calculation. Must be set for sharded databases.',
|
| 252 |
+
lower_bound=0.0,
|
| 253 |
+
)
|
| 254 |
+
_RFAM_DATABASE_PATH = flags.DEFINE_string(
|
| 255 |
+
'rfam_database_path',
|
| 256 |
+
'${DB_DIR}/rfam_14_9_clust_seq_id_90_cov_80_rep_seq.fasta',
|
| 257 |
+
'Rfam database path, used for RNA MSA search.',
|
| 258 |
+
)
|
| 259 |
+
_RFAM_Z_VALUE = flags.DEFINE_float(
|
| 260 |
+
'rfam_z_value',
|
| 261 |
+
None,
|
| 262 |
+
'The Z-value representing the database size in megabases for E-value'
|
| 263 |
+
' calculation. Must be set for sharded databases.',
|
| 264 |
+
lower_bound=0.0,
|
| 265 |
+
)
|
| 266 |
+
_RNA_CENTRAL_DATABASE_PATH = flags.DEFINE_string(
|
| 267 |
+
'rna_central_database_path',
|
| 268 |
+
'${DB_DIR}/rnacentral_active_seq_id_90_cov_80_linclust.fasta',
|
| 269 |
+
'RNAcentral database path, used for RNA MSA search.',
|
| 270 |
+
)
|
| 271 |
+
_RNA_CENTRAL_Z_VALUE = flags.DEFINE_float(
|
| 272 |
+
'rna_central_z_value',
|
| 273 |
+
None,
|
| 274 |
+
'The Z-value representing the database size in megabases for E-value'
|
| 275 |
+
' calculation. Must be set for sharded databases.',
|
| 276 |
+
lower_bound=0.0,
|
| 277 |
+
)
|
| 278 |
+
_PDB_DATABASE_PATH = flags.DEFINE_string(
|
| 279 |
+
'pdb_database_path',
|
| 280 |
+
'${DB_DIR}/mmcif_files',
|
| 281 |
+
'PDB database directory with mmCIF files path, used for template search.',
|
| 282 |
+
)
|
| 283 |
+
_SEQRES_DATABASE_PATH = flags.DEFINE_string(
|
| 284 |
+
'seqres_database_path',
|
| 285 |
+
'${DB_DIR}/pdb_seqres_2022_09_28.fasta',
|
| 286 |
+
'PDB sequence database path, used for template search.',
|
| 287 |
+
)
|
| 288 |
+
|
| 289 |
+
# MMSEQS Database paths.
|
| 290 |
+
_MMSEQS_SMALL_BFD_DATABASE_PATH = flags.DEFINE_string(
|
| 291 |
+
'mmseqs_small_bfd_database_path',
|
| 292 |
+
'${MMSEQS_DB_DIR}/small_bfd_db',
|
| 293 |
+
'Small BFD database path, used for protein MSA search.',
|
| 294 |
+
)
|
| 295 |
+
_MMSEQS_MGNIFY_DATABASE_PATH = flags.DEFINE_string(
|
| 296 |
+
'mmseqs_mgnify_database_path',
|
| 297 |
+
'${MMSEQS_DB_DIR}/mgnify_db',
|
| 298 |
+
'Mgnify database path, used for protein MSA search.',
|
| 299 |
+
)
|
| 300 |
+
_MMSEQS_UNIPROT_CLUSTER_ANNOT_DATABASE_PATH = flags.DEFINE_string(
|
| 301 |
+
'mmseqs_uniprot_cluster_annot_database_path',
|
| 302 |
+
'${MMSEQS_DB_DIR}/uniprot_cluster_annot_db',
|
| 303 |
+
'UniProt database path, used for protein paired MSA search.',
|
| 304 |
+
)
|
| 305 |
+
_MMSEQS_UNIREF90_DATABASE_PATH = flags.DEFINE_string(
|
| 306 |
+
'mmseqs_uniref90_database_path',
|
| 307 |
+
'${MMSEQS_DB_DIR}/uniref90_db',
|
| 308 |
+
'UniRef90 database path, used for MSA search. ',
|
| 309 |
+
)
|
| 310 |
+
|
| 311 |
+
_JACKHMMER_MAX_THREADS = flags.DEFINE_integer(
|
| 312 |
+
'jackhmmer_max_threads',
|
| 313 |
+
None,
|
| 314 |
+
'Maximum number of threads used when running sharded databases. If unset,'
|
| 315 |
+
' defaults to None (no limit).',
|
| 316 |
+
lower_bound=1,
|
| 317 |
+
)
|
| 318 |
+
# Number of CPUs to use for MSA tools.
|
| 319 |
+
_JACKHMMER_N_CPU = flags.DEFINE_integer(
|
| 320 |
+
'jackhmmer_n_cpu',
|
| 321 |
+
# Unfortunately, os.process_cpu_count() is only available in Python 3.13+.
|
| 322 |
+
min(_available_cpu_count(), 8),
|
| 323 |
+
'Number of CPUs to use for Jackhmmer. Defaults to min(cpu_count, 8). Going'
|
| 324 |
+
' above 8 CPUs provides very little additional speedup.',
|
| 325 |
+
lower_bound=0,
|
| 326 |
+
)
|
| 327 |
+
_JACKHMMER_MAX_PARALLEL_SHARDS = flags.DEFINE_integer(
|
| 328 |
+
'jackhmmer_max_parallel_shards',
|
| 329 |
+
None,
|
| 330 |
+
'Maximum number of shards to search against in parallel. If unset, one'
|
| 331 |
+
' Jackhmmer instance will be run per shard. Only applicable if the'
|
| 332 |
+
' database is sharded.',
|
| 333 |
+
lower_bound=1,
|
| 334 |
+
)
|
| 335 |
+
_NHMMER_N_CPU = flags.DEFINE_integer(
|
| 336 |
+
'nhmmer_n_cpu',
|
| 337 |
+
# Unfortunately, os.process_cpu_count() is only available in Python 3.13+.
|
| 338 |
+
min(_available_cpu_count(), 8),
|
| 339 |
+
'Number of CPUs to use for Nhmmer. Defaults to min(cpu_count, 8). Going'
|
| 340 |
+
' above 8 CPUs provides very little additional speedup.',
|
| 341 |
+
lower_bound=0,
|
| 342 |
+
)
|
| 343 |
+
_NHMMER_MAX_PARALLEL_SHARDS = flags.DEFINE_integer(
|
| 344 |
+
'nhmmer_max_parallel_shards',
|
| 345 |
+
None,
|
| 346 |
+
'Maximum number of shards to search against in parallel. If unset, one'
|
| 347 |
+
' Nhmmer instance will be run per shard. Only applicable if the'
|
| 348 |
+
' database is sharded.',
|
| 349 |
+
lower_bound=1,
|
| 350 |
+
)
|
| 351 |
+
_NHMMER_MAX_THREADS = flags.DEFINE_integer(
|
| 352 |
+
'nhmmer_max_threads',
|
| 353 |
+
None,
|
| 354 |
+
'Maximum number of threads used when running sharded databases. If unset,'
|
| 355 |
+
' defaults to None (no limit).',
|
| 356 |
+
lower_bound=1,
|
| 357 |
+
)
|
| 358 |
+
# Data pipeline configuration.
|
| 359 |
+
_RESOLVE_MSA_OVERLAPS = flags.DEFINE_bool(
|
| 360 |
+
'resolve_msa_overlaps',
|
| 361 |
+
True,
|
| 362 |
+
'Whether to deduplicate unpaired MSA against paired MSA. The default'
|
| 363 |
+
' behaviour matches the method described in the AlphaFold 3 paper. Set this'
|
| 364 |
+
' to false if providing custom paired MSA using the unpaired MSA field to'
|
| 365 |
+
' keep it exactly as is as deduplication against the paired MSA could break'
|
| 366 |
+
' the manually crafted pairing between MSA sequences.',
|
| 367 |
+
)
|
| 368 |
+
_MMSEQS_N_CPU = flags.DEFINE_integer(
|
| 369 |
+
'mmseqs_n_cpu',
|
| 370 |
+
min(multiprocessing.cpu_count(), 8),
|
| 371 |
+
'Number of CPUs to use for MMseqs. Default to min(cpu_count, 8). Going'
|
| 372 |
+
' beyond 8 CPUs provides very little additional speedup.',
|
| 373 |
+
)
|
| 374 |
+
|
| 375 |
+
# Template search configuration.
|
| 376 |
+
_MAX_TEMPLATE_DATE = flags.DEFINE_string(
|
| 377 |
+
'max_template_date',
|
| 378 |
+
'2021-09-30', # By default, use the date from the AlphaFold 3 paper.
|
| 379 |
+
'Maximum template release date to consider. Format: YYYY-MM-DD. All'
|
| 380 |
+
' templates released after this date will be ignored. Controls also whether'
|
| 381 |
+
' to allow use of model coordinates for a chemical component from the CCD'
|
| 382 |
+
' if RDKit conformer generation fails and the component does not have ideal'
|
| 383 |
+
' coordinates set. Only for components that have been released before this'
|
| 384 |
+
' date the model coordinates can be used as a fallback.',
|
| 385 |
+
)
|
| 386 |
+
|
| 387 |
+
_CONFORMER_MAX_ITERATIONS = flags.DEFINE_integer(
|
| 388 |
+
'conformer_max_iterations',
|
| 389 |
+
None, # Default to RDKit default parameters value.
|
| 390 |
+
'Optional override for maximum number of iterations to run for RDKit '
|
| 391 |
+
'conformer search.',
|
| 392 |
+
lower_bound=0,
|
| 393 |
+
)
|
| 394 |
+
|
| 395 |
+
# JAX inference performance tuning.
|
| 396 |
+
_JAX_COMPILATION_CACHE_DIR = flags.DEFINE_string(
|
| 397 |
+
'jax_compilation_cache_dir',
|
| 398 |
+
None,
|
| 399 |
+
'Path to a directory for the JAX compilation cache.',
|
| 400 |
+
)
|
| 401 |
+
_GPU_DEVICE = flags.DEFINE_integer(
|
| 402 |
+
'gpu_device',
|
| 403 |
+
0,
|
| 404 |
+
'Optional override for the GPU device to use for inference, uses zero-based'
|
| 405 |
+
' indexing. Defaults to the 0th GPU on the system. Useful on multi-GPU'
|
| 406 |
+
' systems to pin each run to a specific GPU. Note that if GPUs are already'
|
| 407 |
+
' pre-filtered by the environment (e.g. by using CUDA_VISIBLE_DEVICES),'
|
| 408 |
+
' this flag refers to the GPU index after the filtering has been done.',
|
| 409 |
+
)
|
| 410 |
+
_BUCKETS = flags.DEFINE_list(
|
| 411 |
+
'buckets',
|
| 412 |
+
# pyformat: disable
|
| 413 |
+
['256', '512', '768', '1024', '1280', '1536', '2048', '2560', '3072',
|
| 414 |
+
'3584', '4096', '4608', '5120'],
|
| 415 |
+
# pyformat: enable
|
| 416 |
+
'Strictly increasing order of token sizes for which to cache compilations.'
|
| 417 |
+
' For any input with more tokens than the largest bucket size, a new bucket'
|
| 418 |
+
' is created for exactly that number of tokens.',
|
| 419 |
+
)
|
| 420 |
+
_FLASH_ATTENTION_IMPLEMENTATION = flags.DEFINE_enum(
|
| 421 |
+
'flash_attention_implementation',
|
| 422 |
+
default='triton',
|
| 423 |
+
enum_values=['triton', 'cudnn', 'xla', 'cutlass',],
|
| 424 |
+
help=(
|
| 425 |
+
"Flash attention implementation to use. 'triton' and 'cudnn' uses a"
|
| 426 |
+
' Triton and cuDNN flash attention implementation, respectively. The'
|
| 427 |
+
' Triton kernel is fastest and has been tested more thoroughly. The'
|
| 428 |
+
" Triton and cuDNN kernels require Ampere GPUs or later. 'xla' uses an"
|
| 429 |
+
' XLA attention implementation (no flash attention) and is portable'
|
| 430 |
+
' across GPU devices.'
|
| 431 |
+
),
|
| 432 |
+
)
|
| 433 |
+
_NUM_RECYCLES = flags.DEFINE_integer(
|
| 434 |
+
'num_recycles',
|
| 435 |
+
10,
|
| 436 |
+
'Number of recycles to use during inference.',
|
| 437 |
+
lower_bound=1,
|
| 438 |
+
)
|
| 439 |
+
_NUM_DIFFUSION_SAMPLES = flags.DEFINE_integer(
|
| 440 |
+
'num_diffusion_samples',
|
| 441 |
+
5,
|
| 442 |
+
'Number of diffusion samples to generate.',
|
| 443 |
+
lower_bound=1,
|
| 444 |
+
)
|
| 445 |
+
_NUM_SEEDS = flags.DEFINE_integer(
|
| 446 |
+
'num_seeds',
|
| 447 |
+
None,
|
| 448 |
+
'Number of seeds to use for inference. If set, only a single seed must be'
|
| 449 |
+
' provided in the input JSON. AlphaFold 3 will then generate random seeds'
|
| 450 |
+
' in sequence, starting from the single seed specified in the input JSON.'
|
| 451 |
+
' The full input JSON produced by AlphaFold 3 will include the generated'
|
| 452 |
+
' random seeds. If not set, AlphaFold 3 will use the seeds as provided in'
|
| 453 |
+
' the input JSON.',
|
| 454 |
+
lower_bound=1,
|
| 455 |
+
)
|
| 456 |
+
|
| 457 |
+
# Output controls.
|
| 458 |
+
_SAVE_EMBEDDINGS = flags.DEFINE_bool(
|
| 459 |
+
'save_embeddings',
|
| 460 |
+
False,
|
| 461 |
+
'Whether to save the final trunk single and pair embeddings in the output.'
|
| 462 |
+
' Note that the embeddings are large float16 arrays: num_tokens * 384'
|
| 463 |
+
' + num_tokens * num_tokens * 128.',
|
| 464 |
+
)
|
| 465 |
+
_SAVE_DISTOGRAM = flags.DEFINE_bool(
|
| 466 |
+
'save_distogram',
|
| 467 |
+
False,
|
| 468 |
+
'Whether to save the final distogram in the output. Note that the distogram'
|
| 469 |
+
' is a large float16 array: num_tokens * num_tokens * 64.',
|
| 470 |
+
)
|
| 471 |
+
_FORCE_OUTPUT_DIR = flags.DEFINE_bool(
|
| 472 |
+
'force_output_dir',
|
| 473 |
+
False,
|
| 474 |
+
'Whether to force the output directory to be used even if it already exists'
|
| 475 |
+
' and is non-empty. Useful to set this to True to run the data pipeline and'
|
| 476 |
+
' the inference separately, but use the same output directory.',
|
| 477 |
+
)
|
| 478 |
+
|
| 479 |
+
|
| 480 |
+
def make_model_config(
|
| 481 |
+
*,
|
| 482 |
+
flash_attention_implementation: attention.Implementation = 'triton',
|
| 483 |
+
num_diffusion_samples: int = 5,
|
| 484 |
+
num_recycles: int = 10,
|
| 485 |
+
return_embeddings: bool = False,
|
| 486 |
+
return_distogram: bool = False,
|
| 487 |
+
) -> model.Model.Config:
|
| 488 |
+
"""Returns a model config with some defaults overridden."""
|
| 489 |
+
config = model.Model.Config()
|
| 490 |
+
config.global_config.flash_attention_implementation = (
|
| 491 |
+
flash_attention_implementation
|
| 492 |
+
)
|
| 493 |
+
config.heads.diffusion.eval.num_samples = num_diffusion_samples
|
| 494 |
+
config.num_recycles = num_recycles
|
| 495 |
+
config.return_embeddings = return_embeddings
|
| 496 |
+
config.return_distogram = return_distogram
|
| 497 |
+
return config
|
| 498 |
+
|
| 499 |
+
|
| 500 |
+
class ModelRunner:
|
| 501 |
+
"""Helper class to run structure prediction stages."""
|
| 502 |
+
|
| 503 |
+
def __init__(
|
| 504 |
+
self,
|
| 505 |
+
config: model.Model.Config,
|
| 506 |
+
device: jax.Device,
|
| 507 |
+
model_dir: pathlib.Path,
|
| 508 |
+
):
|
| 509 |
+
self._model_config = config
|
| 510 |
+
self._device = device
|
| 511 |
+
self._model_dir = model_dir
|
| 512 |
+
|
| 513 |
+
@functools.cached_property
|
| 514 |
+
def model_params(self) -> hk.Params:
|
| 515 |
+
"""Loads model parameters from the model directory."""
|
| 516 |
+
return params.get_model_haiku_params(model_dir=self._model_dir)
|
| 517 |
+
|
| 518 |
+
@functools.cached_property
|
| 519 |
+
def _model(
|
| 520 |
+
self,
|
| 521 |
+
) -> Callable[[jnp.ndarray, features.BatchDict], model.ModelResult]:
|
| 522 |
+
"""Loads model parameters and returns a jitted model forward pass."""
|
| 523 |
+
|
| 524 |
+
@hk.transform
|
| 525 |
+
def forward_fn(batch):
|
| 526 |
+
return model.Model(self._model_config)(batch)
|
| 527 |
+
|
| 528 |
+
return functools.partial(
|
| 529 |
+
jax.jit(forward_fn.apply, device=self._device), self.model_params
|
| 530 |
+
)
|
| 531 |
+
|
| 532 |
+
def run_inference(
|
| 533 |
+
self, featurised_example: features.BatchDict, rng_key: jnp.ndarray
|
| 534 |
+
) -> model.ModelResult:
|
| 535 |
+
"""Computes a forward pass of the model on a featurised example."""
|
| 536 |
+
featurised_example = jax.device_put(
|
| 537 |
+
jax.tree_util.tree_map(
|
| 538 |
+
jnp.asarray, utils.remove_invalidly_typed_feats(featurised_example)
|
| 539 |
+
),
|
| 540 |
+
self._device,
|
| 541 |
+
)
|
| 542 |
+
|
| 543 |
+
result = self._model(rng_key, featurised_example)
|
| 544 |
+
result = jax.tree.map(np.asarray, result)
|
| 545 |
+
result = jax.tree.map(
|
| 546 |
+
lambda x: x.astype(jnp.float32) if x.dtype == jnp.bfloat16 else x,
|
| 547 |
+
result,
|
| 548 |
+
)
|
| 549 |
+
result = dict(result)
|
| 550 |
+
identifier = self.model_params['__meta__']['__identifier__'].tobytes()
|
| 551 |
+
result['__identifier__'] = identifier
|
| 552 |
+
return result
|
| 553 |
+
|
| 554 |
+
def extract_inference_results(
|
| 555 |
+
self,
|
| 556 |
+
batch: features.BatchDict,
|
| 557 |
+
result: model.ModelResult,
|
| 558 |
+
target_name: str,
|
| 559 |
+
) -> list[model.InferenceResult]:
|
| 560 |
+
"""Extracts inference results from model outputs."""
|
| 561 |
+
return list(
|
| 562 |
+
model.Model.get_inference_result(
|
| 563 |
+
batch=batch, result=result, target_name=target_name
|
| 564 |
+
)
|
| 565 |
+
)
|
| 566 |
+
|
| 567 |
+
def extract_embeddings(
|
| 568 |
+
self, result: model.ModelResult, num_tokens: int
|
| 569 |
+
) -> dict[str, np.ndarray] | None:
|
| 570 |
+
"""Extracts embeddings from model outputs."""
|
| 571 |
+
embeddings = {}
|
| 572 |
+
if 'single_embeddings' in result:
|
| 573 |
+
embeddings['single_embeddings'] = result['single_embeddings'][
|
| 574 |
+
:num_tokens
|
| 575 |
+
].astype(np.float16)
|
| 576 |
+
if 'pair_embeddings' in result:
|
| 577 |
+
embeddings['pair_embeddings'] = result['pair_embeddings'][
|
| 578 |
+
:num_tokens, :num_tokens
|
| 579 |
+
].astype(np.float16)
|
| 580 |
+
return embeddings or None
|
| 581 |
+
|
| 582 |
+
def extract_distogram(
|
| 583 |
+
self, result: model.ModelResult, num_tokens: int
|
| 584 |
+
) -> np.ndarray | None:
|
| 585 |
+
"""Extracts distogram from model outputs."""
|
| 586 |
+
if 'distogram' not in result['distogram']:
|
| 587 |
+
return None
|
| 588 |
+
distogram = result['distogram']['distogram'][:num_tokens, :num_tokens, :]
|
| 589 |
+
return distogram
|
| 590 |
+
|
| 591 |
+
|
| 592 |
+
@dataclasses.dataclass(frozen=True, slots=True, kw_only=True)
|
| 593 |
+
class ResultsForSeed:
|
| 594 |
+
"""Stores the inference results (diffusion samples) for a single seed.
|
| 595 |
+
|
| 596 |
+
Attributes:
|
| 597 |
+
seed: The seed used to generate the samples.
|
| 598 |
+
inference_results: The inference results, one per sample.
|
| 599 |
+
full_fold_input: The fold input that must also include the results of
|
| 600 |
+
running the data pipeline - MSA and templates.
|
| 601 |
+
embeddings: The final trunk single and pair embeddings, if requested.
|
| 602 |
+
distogram: The token distance histogram, if requested.
|
| 603 |
+
"""
|
| 604 |
+
|
| 605 |
+
seed: int
|
| 606 |
+
inference_results: Sequence[model.InferenceResult]
|
| 607 |
+
full_fold_input: folding_input.Input
|
| 608 |
+
embeddings: dict[str, np.ndarray] | None = None
|
| 609 |
+
distogram: np.ndarray | None = None
|
| 610 |
+
|
| 611 |
+
|
| 612 |
+
def predict_structure(
|
| 613 |
+
fold_input: folding_input.Input,
|
| 614 |
+
model_runner: ModelRunner,
|
| 615 |
+
buckets: Sequence[int] | None = None,
|
| 616 |
+
ref_max_modified_date: datetime.date | None = None,
|
| 617 |
+
conformer_max_iterations: int | None = None,
|
| 618 |
+
resolve_msa_overlaps: bool = True,
|
| 619 |
+
) -> Sequence[ResultsForSeed]:
|
| 620 |
+
"""Runs the full inference pipeline to predict structures for each seed."""
|
| 621 |
+
|
| 622 |
+
print(f'Featurising data with {len(fold_input.rng_seeds)} seed(s)...')
|
| 623 |
+
featurisation_start_time = time.time()
|
| 624 |
+
ccd = chemical_components.Ccd(user_ccd=fold_input.user_ccd)
|
| 625 |
+
featurised_examples = featurisation.featurise_input(
|
| 626 |
+
fold_input=fold_input,
|
| 627 |
+
buckets=buckets,
|
| 628 |
+
ccd=ccd,
|
| 629 |
+
verbose=True,
|
| 630 |
+
ref_max_modified_date=ref_max_modified_date,
|
| 631 |
+
conformer_max_iterations=conformer_max_iterations,
|
| 632 |
+
resolve_msa_overlaps=resolve_msa_overlaps,
|
| 633 |
+
)
|
| 634 |
+
print(
|
| 635 |
+
f'Featurising data with {len(fold_input.rng_seeds)} seed(s) took'
|
| 636 |
+
f' {time.time() - featurisation_start_time:.2f} seconds.'
|
| 637 |
+
)
|
| 638 |
+
print(
|
| 639 |
+
'Running model inference and extracting output structure samples with'
|
| 640 |
+
f' {len(fold_input.rng_seeds)} seed(s)...'
|
| 641 |
+
)
|
| 642 |
+
all_inference_start_time = time.time()
|
| 643 |
+
all_inference_results = []
|
| 644 |
+
for seed, example in zip(fold_input.rng_seeds, featurised_examples):
|
| 645 |
+
print(f'Running model inference with seed {seed}...')
|
| 646 |
+
inference_start_time = time.time()
|
| 647 |
+
rng_key = jax.random.PRNGKey(seed)
|
| 648 |
+
result = model_runner.run_inference(example, rng_key)
|
| 649 |
+
print(
|
| 650 |
+
f'Running model inference with seed {seed} took'
|
| 651 |
+
f' {time.time() - inference_start_time:.2f} seconds.'
|
| 652 |
+
)
|
| 653 |
+
print(f'Extracting inference results with seed {seed}...')
|
| 654 |
+
extract_structures = time.time()
|
| 655 |
+
inference_results = model_runner.extract_inference_results(
|
| 656 |
+
batch=example, result=result, target_name=fold_input.name
|
| 657 |
+
)
|
| 658 |
+
num_tokens = len(inference_results[0].metadata['token_chain_ids'])
|
| 659 |
+
embeddings = model_runner.extract_embeddings(
|
| 660 |
+
result=result, num_tokens=num_tokens
|
| 661 |
+
)
|
| 662 |
+
distogram = model_runner.extract_distogram(
|
| 663 |
+
result=result, num_tokens=num_tokens
|
| 664 |
+
)
|
| 665 |
+
print(
|
| 666 |
+
f'Extracting {len(inference_results)} inference samples with'
|
| 667 |
+
f' seed {seed} took {time.time() - extract_structures:.2f} seconds.'
|
| 668 |
+
)
|
| 669 |
+
|
| 670 |
+
all_inference_results.append(
|
| 671 |
+
ResultsForSeed(
|
| 672 |
+
seed=seed,
|
| 673 |
+
inference_results=inference_results,
|
| 674 |
+
full_fold_input=fold_input,
|
| 675 |
+
embeddings=embeddings,
|
| 676 |
+
distogram=distogram,
|
| 677 |
+
)
|
| 678 |
+
)
|
| 679 |
+
print(
|
| 680 |
+
'Running model inference and extracting output structures with'
|
| 681 |
+
f' {len(fold_input.rng_seeds)} seed(s) took'
|
| 682 |
+
f' {time.time() - all_inference_start_time:.2f} seconds.'
|
| 683 |
+
)
|
| 684 |
+
return all_inference_results
|
| 685 |
+
|
| 686 |
+
|
| 687 |
+
def write_fold_input_json(
|
| 688 |
+
fold_input: folding_input.Input,
|
| 689 |
+
output_dir: os.PathLike[str] | str,
|
| 690 |
+
) -> None:
|
| 691 |
+
"""Writes the input JSON to the output directory."""
|
| 692 |
+
os.makedirs(output_dir, exist_ok=True)
|
| 693 |
+
path = os.path.join(output_dir, f'{fold_input.sanitised_name()}_data.json')
|
| 694 |
+
print(f'Writing model input JSON to {path}')
|
| 695 |
+
with open(path, 'wt') as f:
|
| 696 |
+
f.write(fold_input.to_json())
|
| 697 |
+
|
| 698 |
+
|
| 699 |
+
def write_outputs(
|
| 700 |
+
all_inference_results: Sequence[ResultsForSeed],
|
| 701 |
+
output_dir: os.PathLike[str] | str,
|
| 702 |
+
job_name: str,
|
| 703 |
+
) -> None:
|
| 704 |
+
"""Writes outputs to the specified output directory."""
|
| 705 |
+
ranking_scores = []
|
| 706 |
+
max_ranking_score = None
|
| 707 |
+
max_ranking_result = None
|
| 708 |
+
try:
|
| 709 |
+
output_terms = (
|
| 710 |
+
pathlib.Path(af3_cpp.__file__).parent / 'OUTPUT_TERMS_OF_USE.md'
|
| 711 |
+
).read_text()
|
| 712 |
+
except FileNotFoundError:
|
| 713 |
+
output_terms = None
|
| 714 |
+
os.makedirs(output_dir, exist_ok=True)
|
| 715 |
+
for results_for_seed in all_inference_results:
|
| 716 |
+
seed = results_for_seed.seed
|
| 717 |
+
for sample_idx, result in enumerate(results_for_seed.inference_results):
|
| 718 |
+
sample_dir = os.path.join(output_dir, f'seed-{seed}_sample-{sample_idx}')
|
| 719 |
+
os.makedirs(sample_dir, exist_ok=True)
|
| 720 |
+
post_processing.write_output(
|
| 721 |
+
inference_result=result,
|
| 722 |
+
output_dir=sample_dir,
|
| 723 |
+
name=f'{job_name}_seed-{seed}_sample-{sample_idx}',
|
| 724 |
+
)
|
| 725 |
+
ranking_score = float(result.metadata['ranking_score'])
|
| 726 |
+
ranking_scores.append((seed, sample_idx, ranking_score))
|
| 727 |
+
if max_ranking_score is None or ranking_score > max_ranking_score:
|
| 728 |
+
max_ranking_score = ranking_score
|
| 729 |
+
max_ranking_result = result
|
| 730 |
+
|
| 731 |
+
if embeddings := results_for_seed.embeddings:
|
| 732 |
+
embeddings_dir = os.path.join(output_dir, f'seed-{seed}_embeddings')
|
| 733 |
+
os.makedirs(embeddings_dir, exist_ok=True)
|
| 734 |
+
post_processing.write_embeddings(
|
| 735 |
+
embeddings=embeddings,
|
| 736 |
+
output_dir=embeddings_dir,
|
| 737 |
+
name=f'{job_name}_seed-{seed}',
|
| 738 |
+
)
|
| 739 |
+
|
| 740 |
+
if (distogram := results_for_seed.distogram) is not None:
|
| 741 |
+
distogram_dir = os.path.join(output_dir, f'seed-{seed}_distogram')
|
| 742 |
+
os.makedirs(distogram_dir, exist_ok=True)
|
| 743 |
+
distogram_path = os.path.join(
|
| 744 |
+
distogram_dir, f'{job_name}_seed-{seed}_distogram.npz'
|
| 745 |
+
)
|
| 746 |
+
with open(distogram_path, 'wb') as f:
|
| 747 |
+
np.savez_compressed(f, distogram=distogram.astype(np.float16))
|
| 748 |
+
|
| 749 |
+
if max_ranking_result is not None: # True iff ranking_scores non-empty.
|
| 750 |
+
post_processing.write_output(
|
| 751 |
+
inference_result=max_ranking_result,
|
| 752 |
+
output_dir=output_dir,
|
| 753 |
+
# The output terms of use are the same for all seeds/samples.
|
| 754 |
+
terms_of_use=output_terms,
|
| 755 |
+
name=job_name,
|
| 756 |
+
)
|
| 757 |
+
# Save csv of ranking scores with seeds and sample indices, to allow easier
|
| 758 |
+
# comparison of ranking scores across different runs.
|
| 759 |
+
with open(
|
| 760 |
+
os.path.join(output_dir, f'{job_name}_ranking_scores.csv'), 'wt'
|
| 761 |
+
) as f:
|
| 762 |
+
writer = csv.writer(f)
|
| 763 |
+
writer.writerow(['seed', 'sample', 'ranking_score'])
|
| 764 |
+
writer.writerows(ranking_scores)
|
| 765 |
+
|
| 766 |
+
|
| 767 |
+
def replace_db_dir(path_with_db_dir: str, db_dirs: Sequence[str]) -> str:
|
| 768 |
+
"""Replaces the DB_DIR placeholder in a path with the given DB_DIR."""
|
| 769 |
+
template = string.Template(path_with_db_dir)
|
| 770 |
+
if 'DB_DIR' in template.get_identifiers():
|
| 771 |
+
for db_dir in db_dirs:
|
| 772 |
+
path = template.substitute(DB_DIR=db_dir)
|
| 773 |
+
if os.path.exists(path):
|
| 774 |
+
return path
|
| 775 |
+
raise FileNotFoundError(
|
| 776 |
+
f'{path_with_db_dir} with ${{DB_DIR}} not found in any of {db_dirs}.'
|
| 777 |
+
)
|
| 778 |
+
if not os.path.exists(path_with_db_dir):
|
| 779 |
+
raise FileNotFoundError(f'{path_with_db_dir} does not exist.')
|
| 780 |
+
return path_with_db_dir
|
| 781 |
+
|
| 782 |
+
|
| 783 |
+
def replace_mmseqs_db_dir(
|
| 784 |
+
path_with_db_dir: str,
|
| 785 |
+
db_dirs: Sequence[str],
|
| 786 |
+
mmseqs_db_dirs: Sequence[str]
|
| 787 |
+
) -> str:
|
| 788 |
+
"""Replaces the MMSEQS_DB_DIR placeholder in a path with the given MMSEQS_DB_DIR.
|
| 789 |
+
|
| 790 |
+
Args:
|
| 791 |
+
path_with_db_dir: Path containing MMSEQS_DB_DIR placeholder
|
| 792 |
+
db_dirs: List of database directories
|
| 793 |
+
mmseqs_db_dirs: List of mmseqs database directories
|
| 794 |
+
use_gpu: Whether to use GPU version of mmseqs databases
|
| 795 |
+
|
| 796 |
+
Returns:
|
| 797 |
+
The expanded path if found, otherwise raises FileNotFoundError
|
| 798 |
+
"""
|
| 799 |
+
template = string.Template(path_with_db_dir)
|
| 800 |
+
|
| 801 |
+
is_jackhmmer_db = any(
|
| 802 |
+
path_with_db_dir.endswith(db) for db in [
|
| 803 |
+
'bfd-first_non_consensus_sequences.fasta',
|
| 804 |
+
'mgy_clusters_2022_05.fa',
|
| 805 |
+
'uniprot_all_2021_04.fa',
|
| 806 |
+
'uniref90_2022_05.fa'
|
| 807 |
+
]
|
| 808 |
+
)
|
| 809 |
+
|
| 810 |
+
if 'MMSEQS_DB_DIR' in template.get_identifiers():
|
| 811 |
+
db_suffixes = [
|
| 812 |
+
'small_bfd_db',
|
| 813 |
+
'mgnify_db',
|
| 814 |
+
'uniprot_cluster_annot_db',
|
| 815 |
+
'uniref90_db'
|
| 816 |
+
]
|
| 817 |
+
|
| 818 |
+
is_mmseqs_db = any(
|
| 819 |
+
path_with_db_dir.endswith(suffix) for suffix in db_suffixes
|
| 820 |
+
)
|
| 821 |
+
|
| 822 |
+
for mmseqs_db_dir in mmseqs_db_dirs:
|
| 823 |
+
path = template.substitute(MMSEQS_DB_DIR=mmseqs_db_dir)
|
| 824 |
+
if is_mmseqs_db:
|
| 825 |
+
if os.path.exists(path):
|
| 826 |
+
return path
|
| 827 |
+
else:
|
| 828 |
+
return path
|
| 829 |
+
|
| 830 |
+
if is_mmseqs_db:
|
| 831 |
+
raise FileNotFoundError(
|
| 832 |
+
f'{path_with_db_dir} with ${{MMSEQS_DB_DIR}} not found in any of {mmseqs_db_dirs}.'
|
| 833 |
+
)
|
| 834 |
+
return template.substitute(MMSEQS_DB_DIR=mmseqs_db_dirs[0])
|
| 835 |
+
|
| 836 |
+
if 'DB_DIR' in template.get_identifiers():
|
| 837 |
+
for db_dir in db_dirs:
|
| 838 |
+
path = template.substitute(DB_DIR=db_dir)
|
| 839 |
+
if is_jackhmmer_db:
|
| 840 |
+
return path
|
| 841 |
+
if os.path.exists(path):
|
| 842 |
+
return path
|
| 843 |
+
if is_jackhmmer_db:
|
| 844 |
+
return template.substitute(DB_DIR=db_dirs[0])
|
| 845 |
+
raise FileNotFoundError(
|
| 846 |
+
f'{path_with_db_dir} with ${{DB_DIR}} not found in any of {db_dirs}.'
|
| 847 |
+
)
|
| 848 |
+
if not is_jackhmmer_db and not os.path.exists(path_with_db_dir):
|
| 849 |
+
raise FileNotFoundError(f'{path_with_db_dir} does not exist.')
|
| 850 |
+
return path_with_db_dir
|
| 851 |
+
|
| 852 |
+
|
| 853 |
+
@overload
|
| 854 |
+
def process_fold_input(
|
| 855 |
+
fold_input: folding_input.Input,
|
| 856 |
+
data_pipeline_config: pipeline.DataPipelineConfig | None,
|
| 857 |
+
model_runner: None,
|
| 858 |
+
output_dir: os.PathLike[str] | str,
|
| 859 |
+
buckets: Sequence[int] | None = None,
|
| 860 |
+
ref_max_modified_date: datetime.date | None = None,
|
| 861 |
+
conformer_max_iterations: int | None = None,
|
| 862 |
+
resolve_msa_overlaps: bool = True,
|
| 863 |
+
force_output_dir: bool = False,
|
| 864 |
+
) -> folding_input.Input:
|
| 865 |
+
...
|
| 866 |
+
|
| 867 |
+
|
| 868 |
+
@overload
|
| 869 |
+
def process_fold_input(
|
| 870 |
+
fold_input: folding_input.Input,
|
| 871 |
+
data_pipeline_config: pipeline.DataPipelineConfig | None,
|
| 872 |
+
model_runner: ModelRunner,
|
| 873 |
+
output_dir: os.PathLike[str] | str,
|
| 874 |
+
buckets: Sequence[int] | None = None,
|
| 875 |
+
ref_max_modified_date: datetime.date | None = None,
|
| 876 |
+
conformer_max_iterations: int | None = None,
|
| 877 |
+
resolve_msa_overlaps: bool = True,
|
| 878 |
+
force_output_dir: bool = False,
|
| 879 |
+
) -> Sequence[ResultsForSeed]:
|
| 880 |
+
...
|
| 881 |
+
|
| 882 |
+
|
| 883 |
+
def process_fold_input(
|
| 884 |
+
fold_input: folding_input.Input,
|
| 885 |
+
data_pipeline_config: pipeline.DataPipelineConfig | None,
|
| 886 |
+
model_runner: ModelRunner | None,
|
| 887 |
+
output_dir: os.PathLike[str] | str,
|
| 888 |
+
buckets: Sequence[int] | None = None,
|
| 889 |
+
ref_max_modified_date: datetime.date | None = None,
|
| 890 |
+
conformer_max_iterations: int | None = None,
|
| 891 |
+
resolve_msa_overlaps: bool = True,
|
| 892 |
+
force_output_dir: bool = False,
|
| 893 |
+
) -> folding_input.Input | Sequence[ResultsForSeed]:
|
| 894 |
+
"""Runs data pipeline and/or inference on a single fold input.
|
| 895 |
+
|
| 896 |
+
Args:
|
| 897 |
+
fold_input: Fold input to process.
|
| 898 |
+
data_pipeline_config: Data pipeline config to use. If None, skip the data
|
| 899 |
+
pipeline.
|
| 900 |
+
model_runner: Model runner to use. If None, skip inference.
|
| 901 |
+
output_dir: Output directory to write to.
|
| 902 |
+
buckets: Bucket sizes to pad the data to, to avoid excessive re-compilation
|
| 903 |
+
of the model. If None, calculate the appropriate bucket size from the
|
| 904 |
+
number of tokens. If not None, must be a sequence of at least one integer,
|
| 905 |
+
in strictly increasing order. Will raise an error if the number of tokens
|
| 906 |
+
is more than the largest bucket size.
|
| 907 |
+
ref_max_modified_date: Optional maximum date that controls whether to allow
|
| 908 |
+
use of model coordinates for a chemical component from the CCD if RDKit
|
| 909 |
+
conformer generation fails and the component does not have ideal
|
| 910 |
+
coordinates set. Only for components that have been released before this
|
| 911 |
+
date the model coordinates can be used as a fallback.
|
| 912 |
+
conformer_max_iterations: Optional override for maximum number of iterations
|
| 913 |
+
to run for RDKit conformer search.
|
| 914 |
+
resolve_msa_overlaps: Whether to deduplicate unpaired MSA against paired
|
| 915 |
+
MSA. The default behaviour matches the method described in the AlphaFold 3
|
| 916 |
+
paper. Set this to false if providing custom paired MSA using the unpaired
|
| 917 |
+
MSA field to keep it exactly as is as deduplication against the paired MSA
|
| 918 |
+
could break the manually crafted pairing between MSA sequences.
|
| 919 |
+
force_output_dir: If True, do not create a new output directory even if the
|
| 920 |
+
existing one is non-empty. Instead use the existing output directory and
|
| 921 |
+
potentially overwrite existing files. If False, create a new timestamped
|
| 922 |
+
output directory instead if the existing one is non-empty.
|
| 923 |
+
|
| 924 |
+
Returns:
|
| 925 |
+
The processed fold input, or the inference results for each seed.
|
| 926 |
+
|
| 927 |
+
Raises:
|
| 928 |
+
ValueError: If the fold input has no chains.
|
| 929 |
+
"""
|
| 930 |
+
print(f'\nRunning fold job {fold_input.name}...')
|
| 931 |
+
|
| 932 |
+
if not fold_input.chains:
|
| 933 |
+
raise ValueError('Fold input has no chains.')
|
| 934 |
+
|
| 935 |
+
if (
|
| 936 |
+
not force_output_dir
|
| 937 |
+
and os.path.exists(output_dir)
|
| 938 |
+
and os.listdir(output_dir)
|
| 939 |
+
):
|
| 940 |
+
new_output_dir = (
|
| 941 |
+
f'{output_dir}_{datetime.datetime.now().strftime("%Y%m%d_%H%M%S")}'
|
| 942 |
+
)
|
| 943 |
+
print(
|
| 944 |
+
f'Output will be written in {new_output_dir} since {output_dir} is'
|
| 945 |
+
' non-empty.'
|
| 946 |
+
)
|
| 947 |
+
output_dir = new_output_dir
|
| 948 |
+
else:
|
| 949 |
+
print(f'Output will be written in {output_dir}')
|
| 950 |
+
|
| 951 |
+
# Create model loader callback function
|
| 952 |
+
def load_model_callback():
|
| 953 |
+
if model_runner is not None:
|
| 954 |
+
_ = model_runner.model_params
|
| 955 |
+
|
| 956 |
+
if data_pipeline_config is None:
|
| 957 |
+
print('Skipping data pipeline...')
|
| 958 |
+
# Load model immediately when skipping pipeline
|
| 959 |
+
if model_runner is not None:
|
| 960 |
+
print('Loading model parameters (no pipeline to wait for)...')
|
| 961 |
+
load_model_callback()
|
| 962 |
+
else:
|
| 963 |
+
print('Running data pipeline...')
|
| 964 |
+
fold_input = pipeline.DataPipeline(data_pipeline_config, load_model_callback).process(fold_input)
|
| 965 |
+
|
| 966 |
+
write_fold_input_json(fold_input, output_dir)
|
| 967 |
+
if model_runner is None:
|
| 968 |
+
print('Skipping model inference...')
|
| 969 |
+
output = fold_input
|
| 970 |
+
else:
|
| 971 |
+
print(
|
| 972 |
+
f'Predicting 3D structure for {fold_input.name} with'
|
| 973 |
+
f' {len(fold_input.rng_seeds)} seed(s)...'
|
| 974 |
+
)
|
| 975 |
+
all_inference_results = predict_structure(
|
| 976 |
+
fold_input=fold_input,
|
| 977 |
+
model_runner=model_runner,
|
| 978 |
+
buckets=buckets,
|
| 979 |
+
ref_max_modified_date=ref_max_modified_date,
|
| 980 |
+
conformer_max_iterations=conformer_max_iterations,
|
| 981 |
+
resolve_msa_overlaps=resolve_msa_overlaps,
|
| 982 |
+
)
|
| 983 |
+
print(f'Writing outputs with {len(fold_input.rng_seeds)} seed(s)...')
|
| 984 |
+
write_outputs(
|
| 985 |
+
all_inference_results=all_inference_results,
|
| 986 |
+
output_dir=output_dir,
|
| 987 |
+
job_name=fold_input.sanitised_name(),
|
| 988 |
+
)
|
| 989 |
+
output = all_inference_results
|
| 990 |
+
|
| 991 |
+
print(f'Fold job {fold_input.name} done, output written to {output_dir}\n')
|
| 992 |
+
return output
|
| 993 |
+
|
| 994 |
+
|
| 995 |
+
def main(_):
|
| 996 |
+
if _JAX_COMPILATION_CACHE_DIR.value is not None:
|
| 997 |
+
jax.config.update(
|
| 998 |
+
'jax_compilation_cache_dir', _JAX_COMPILATION_CACHE_DIR.value
|
| 999 |
+
)
|
| 1000 |
+
|
| 1001 |
+
if _JSON_PATH.value is None == _INPUT_DIR.value is None:
|
| 1002 |
+
raise ValueError(
|
| 1003 |
+
'Exactly one of --json_path or --input_dir must be specified.'
|
| 1004 |
+
)
|
| 1005 |
+
|
| 1006 |
+
if not _RUN_INFERENCE.value and not _RUN_DATA_PIPELINE.value:
|
| 1007 |
+
raise ValueError(
|
| 1008 |
+
'At least one of --run_inference or --run_data_pipeline must be'
|
| 1009 |
+
' set to true.'
|
| 1010 |
+
)
|
| 1011 |
+
|
| 1012 |
+
if _INPUT_DIR.value is not None:
|
| 1013 |
+
fold_inputs = folding_input.load_fold_inputs_from_dir(
|
| 1014 |
+
pathlib.Path(_INPUT_DIR.value)
|
| 1015 |
+
)
|
| 1016 |
+
elif _JSON_PATH.value is not None:
|
| 1017 |
+
fold_inputs = folding_input.load_fold_inputs_from_path(
|
| 1018 |
+
pathlib.Path(_JSON_PATH.value)
|
| 1019 |
+
)
|
| 1020 |
+
else:
|
| 1021 |
+
raise AssertionError(
|
| 1022 |
+
'Exactly one of --json_path or --input_dir must be specified.'
|
| 1023 |
+
)
|
| 1024 |
+
|
| 1025 |
+
# Make sure we can create the output directory before running anything.
|
| 1026 |
+
try:
|
| 1027 |
+
os.makedirs(_OUTPUT_DIR.value, exist_ok=True)
|
| 1028 |
+
except OSError as e:
|
| 1029 |
+
print(f'Failed to create output directory {_OUTPUT_DIR.value}: {e}')
|
| 1030 |
+
raise
|
| 1031 |
+
|
| 1032 |
+
# if _RUN_INFERENCE.value:
|
| 1033 |
+
# # Fail early on incompatible devices, but only if we're running inference.
|
| 1034 |
+
# gpu_devices = jax.local_devices(backend='gpu')
|
| 1035 |
+
# if gpu_devices:
|
| 1036 |
+
# compute_capability = float(
|
| 1037 |
+
# gpu_devices[_GPU_DEVICE.value].compute_capability
|
| 1038 |
+
# )
|
| 1039 |
+
# if compute_capability < 6.0:
|
| 1040 |
+
# raise ValueError(
|
| 1041 |
+
# 'AlphaFold 3 requires at least GPU compute capability 6.0 (see'
|
| 1042 |
+
# ' https://developer.nvidia.com/cuda-gpus).'
|
| 1043 |
+
# )
|
| 1044 |
+
# elif 7.0 <= compute_capability < 8.0:
|
| 1045 |
+
# xla_flags = os.environ.get('XLA_FLAGS')
|
| 1046 |
+
# required_flag = '--xla_disable_hlo_passes=custom-kernel-fusion-rewriter'
|
| 1047 |
+
# if not xla_flags or required_flag not in xla_flags:
|
| 1048 |
+
# raise ValueError(
|
| 1049 |
+
# 'For devices with GPU compute capability 7.x (see'
|
| 1050 |
+
# ' https://developer.nvidia.com/cuda-gpus) the ENV XLA_FLAGS must'
|
| 1051 |
+
# f' include "{required_flag}".'
|
| 1052 |
+
# )
|
| 1053 |
+
# if _FLASH_ATTENTION_IMPLEMENTATION.value != 'xla':
|
| 1054 |
+
# raise ValueError(
|
| 1055 |
+
# 'For devices with GPU compute capability 7.x (see'
|
| 1056 |
+
# ' https://developer.nvidia.com/cuda-gpus) the'
|
| 1057 |
+
# ' --flash_attention_implementation must be set to "xla".'
|
| 1058 |
+
# )
|
| 1059 |
+
|
| 1060 |
+
notice = textwrap.wrap(
|
| 1061 |
+
'Running AlphaFold 3. Please note that standard AlphaFold 3 model'
|
| 1062 |
+
' parameters are only available under terms of use provided at'
|
| 1063 |
+
' https://github.com/google-deepmind/alphafold3/blob/main/WEIGHTS_TERMS_OF_USE.md.'
|
| 1064 |
+
' If you do not agree to these terms and are using AlphaFold 3 derived'
|
| 1065 |
+
' model parameters, cancel execution of AlphaFold 3 inference with'
|
| 1066 |
+
' CTRL-C, and do not use the model parameters.',
|
| 1067 |
+
break_long_words=False,
|
| 1068 |
+
break_on_hyphens=False,
|
| 1069 |
+
width=80,
|
| 1070 |
+
)
|
| 1071 |
+
print('\n' + '\n'.join(notice) + '\n')
|
| 1072 |
+
|
| 1073 |
+
max_template_date = datetime.date.fromisoformat(_MAX_TEMPLATE_DATE.value)
|
| 1074 |
+
if _RUN_DATA_PIPELINE.value:
|
| 1075 |
+
if not _USE_MMSEQS.value:
|
| 1076 |
+
expand_path = lambda x: replace_db_dir(x, DB_DIR.value)
|
| 1077 |
+
data_pipeline_config = pipeline.DataPipelineConfig(
|
| 1078 |
+
jackhmmer_binary_path=_JACKHMMER_BINARY_PATH.value,
|
| 1079 |
+
nhmmer_binary_path=_NHMMER_BINARY_PATH.value,
|
| 1080 |
+
hmmalign_binary_path=_HMMALIGN_BINARY_PATH.value,
|
| 1081 |
+
hmmsearch_binary_path=_HMMSEARCH_BINARY_PATH.value,
|
| 1082 |
+
hmmbuild_binary_path=_HMMBUILD_BINARY_PATH.value,
|
| 1083 |
+
mmseqs_binary_path=_MMSEQS_BINARY_PATH.value,
|
| 1084 |
+
small_bfd_database_path=expand_path(_SMALL_BFD_DATABASE_PATH.value),
|
| 1085 |
+
small_bfd_z_value=_SMALL_BFD_Z_VALUE.value,
|
| 1086 |
+
mgnify_database_path=expand_path(_MGNIFY_DATABASE_PATH.value),
|
| 1087 |
+
mgnify_z_value=_MGNIFY_Z_VALUE.value,
|
| 1088 |
+
uniprot_cluster_annot_database_path=expand_path(
|
| 1089 |
+
_UNIPROT_CLUSTER_ANNOT_DATABASE_PATH.value
|
| 1090 |
+
),
|
| 1091 |
+
uniprot_cluster_annot_z_value=_UNIPROT_CLUSTER_ANNOT_Z_VALUE.value,
|
| 1092 |
+
uniref90_database_path=expand_path(_UNIREF90_DATABASE_PATH.value),
|
| 1093 |
+
uniref90_z_value=_UNIREF90_Z_VALUE.value,
|
| 1094 |
+
ntrna_database_path=expand_path(_NTRNA_DATABASE_PATH.value),
|
| 1095 |
+
ntrna_z_value=_NTRNA_Z_VALUE.value,
|
| 1096 |
+
rfam_database_path=expand_path(_RFAM_DATABASE_PATH.value),
|
| 1097 |
+
rfam_z_value=_RFAM_Z_VALUE.value,
|
| 1098 |
+
rna_central_database_path=expand_path(_RNA_CENTRAL_DATABASE_PATH.value),
|
| 1099 |
+
rna_central_z_value=_RNA_CENTRAL_Z_VALUE.value,
|
| 1100 |
+
pdb_database_path=expand_path(_PDB_DATABASE_PATH.value),
|
| 1101 |
+
seqres_database_path=expand_path(_SEQRES_DATABASE_PATH.value),
|
| 1102 |
+
jackhmmer_n_cpu=_JACKHMMER_N_CPU.value,
|
| 1103 |
+
jackhmmer_max_parallel_shards=_JACKHMMER_MAX_PARALLEL_SHARDS.value,
|
| 1104 |
+
jackhmmer_max_threads=_JACKHMMER_MAX_THREADS.value,
|
| 1105 |
+
nhmmer_n_cpu=_NHMMER_N_CPU.value,
|
| 1106 |
+
nhmmer_max_parallel_shards=_NHMMER_MAX_PARALLEL_SHARDS.value,
|
| 1107 |
+
nhmmer_max_threads=_NHMMER_MAX_THREADS.value,
|
| 1108 |
+
max_template_date=max_template_date,
|
| 1109 |
+
use_mmseqs=_USE_MMSEQS.value,
|
| 1110 |
+
mmseqs_options=_MMSEQS_OPTIONS.value,
|
| 1111 |
+
result2msa_options=_R2MSA_OPTIONS.value,
|
| 1112 |
+
)
|
| 1113 |
+
else:
|
| 1114 |
+
expand_path = lambda x: replace_mmseqs_db_dir(x, DB_DIR.value, MMSEQS_DB_DIR.value)
|
| 1115 |
+
data_pipeline_config = pipeline.DataPipelineConfig(
|
| 1116 |
+
jackhmmer_binary_path=_JACKHMMER_BINARY_PATH.value,
|
| 1117 |
+
nhmmer_binary_path=_NHMMER_BINARY_PATH.value,
|
| 1118 |
+
hmmalign_binary_path=_HMMALIGN_BINARY_PATH.value,
|
| 1119 |
+
hmmsearch_binary_path=_HMMSEARCH_BINARY_PATH.value,
|
| 1120 |
+
hmmbuild_binary_path=_HMMBUILD_BINARY_PATH.value,
|
| 1121 |
+
mmseqs_binary_path=_MMSEQS_BINARY_PATH.value,
|
| 1122 |
+
small_bfd_database_path=expand_path(_MMSEQS_SMALL_BFD_DATABASE_PATH.value),
|
| 1123 |
+
mgnify_database_path=expand_path(_MMSEQS_MGNIFY_DATABASE_PATH.value),
|
| 1124 |
+
uniprot_cluster_annot_database_path=expand_path(
|
| 1125 |
+
_MMSEQS_UNIPROT_CLUSTER_ANNOT_DATABASE_PATH.value
|
| 1126 |
+
),
|
| 1127 |
+
uniref90_database_path=expand_path(_MMSEQS_UNIREF90_DATABASE_PATH.value),
|
| 1128 |
+
ntrna_database_path=expand_path(_NTRNA_DATABASE_PATH.value),
|
| 1129 |
+
rfam_database_path=expand_path(_RFAM_DATABASE_PATH.value),
|
| 1130 |
+
rna_central_database_path=expand_path(_RNA_CENTRAL_DATABASE_PATH.value),
|
| 1131 |
+
pdb_database_path=expand_path(_PDB_DATABASE_PATH.value),
|
| 1132 |
+
seqres_database_path=expand_path(_SEQRES_DATABASE_PATH.value),
|
| 1133 |
+
mmseqs_n_cpu=_MMSEQS_N_CPU.value,
|
| 1134 |
+
nhmmer_n_cpu=_NHMMER_N_CPU.value,
|
| 1135 |
+
max_template_date=max_template_date,
|
| 1136 |
+
use_mmseqs=_USE_MMSEQS.value,
|
| 1137 |
+
use_mmseqs_gpu=_USE_MMSEQS_GPU.value,
|
| 1138 |
+
mmseqs_options=_MMSEQS_OPTIONS.value,
|
| 1139 |
+
result2msa_options=_R2MSA_OPTIONS.value,
|
| 1140 |
+
)
|
| 1141 |
+
else:
|
| 1142 |
+
data_pipeline_config = None
|
| 1143 |
+
|
| 1144 |
+
if _RUN_INFERENCE.value:
|
| 1145 |
+
devices = jax.local_devices(backend='gpu')
|
| 1146 |
+
print(
|
| 1147 |
+
f'Found local devices: {devices}, using device {_GPU_DEVICE.value}:'
|
| 1148 |
+
f' {devices[_GPU_DEVICE.value]}'
|
| 1149 |
+
)
|
| 1150 |
+
|
| 1151 |
+
print('Building model from scratch...')
|
| 1152 |
+
model_runner = ModelRunner(
|
| 1153 |
+
config=make_model_config(
|
| 1154 |
+
flash_attention_implementation=typing.cast(
|
| 1155 |
+
attention.Implementation, _FLASH_ATTENTION_IMPLEMENTATION.value
|
| 1156 |
+
),
|
| 1157 |
+
num_diffusion_samples=_NUM_DIFFUSION_SAMPLES.value,
|
| 1158 |
+
num_recycles=_NUM_RECYCLES.value,
|
| 1159 |
+
return_embeddings=_SAVE_EMBEDDINGS.value,
|
| 1160 |
+
return_distogram=_SAVE_DISTOGRAM.value,
|
| 1161 |
+
),
|
| 1162 |
+
device=devices[_GPU_DEVICE.value],
|
| 1163 |
+
model_dir=pathlib.Path(MODEL_DIR.value),
|
| 1164 |
+
)
|
| 1165 |
+
# Check we can load the model parameters before launching anything.
|
| 1166 |
+
print('Checking that model parameters can be loaded...')
|
| 1167 |
+
_ = model_runner.model_params
|
| 1168 |
+
else:
|
| 1169 |
+
model_runner = None
|
| 1170 |
+
|
| 1171 |
+
num_fold_inputs = 0
|
| 1172 |
+
for fold_input in fold_inputs:
|
| 1173 |
+
if _NUM_SEEDS.value is not None:
|
| 1174 |
+
print(f'Expanding fold job {fold_input.name} to {_NUM_SEEDS.value} seeds')
|
| 1175 |
+
fold_input = fold_input.with_multiple_seeds(_NUM_SEEDS.value)
|
| 1176 |
+
process_fold_input(
|
| 1177 |
+
fold_input=fold_input,
|
| 1178 |
+
data_pipeline_config=data_pipeline_config,
|
| 1179 |
+
model_runner=model_runner,
|
| 1180 |
+
output_dir=os.path.join(_OUTPUT_DIR.value, fold_input.sanitised_name()),
|
| 1181 |
+
buckets=tuple(int(bucket) for bucket in _BUCKETS.value),
|
| 1182 |
+
ref_max_modified_date=max_template_date,
|
| 1183 |
+
conformer_max_iterations=_CONFORMER_MAX_ITERATIONS.value,
|
| 1184 |
+
resolve_msa_overlaps=_RESOLVE_MSA_OVERLAPS.value,
|
| 1185 |
+
force_output_dir=_FORCE_OUTPUT_DIR.value,
|
| 1186 |
+
)
|
| 1187 |
+
num_fold_inputs += 1
|
| 1188 |
+
|
| 1189 |
+
print(f'Done running {num_fold_inputs} fold jobs.')
|
| 1190 |
+
|
| 1191 |
+
|
| 1192 |
+
if __name__ == '__main__':
|
| 1193 |
+
flags.mark_flags_as_required(['output_dir'])
|
| 1194 |
+
app.run(main)
|
weight/_dep/abseil-cpp/.github/ISSUE_TEMPLATE/config.yml
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
blank_issues_enabled: false
|
| 2 |
+
contact_links:
|
| 3 |
+
- name: Question
|
| 4 |
+
url: https://github.com/abseil/abseil-cpp/discussions
|
| 5 |
+
about: Have a question? Ask us anything! :-)
|
weight/_dep/abseil-cpp/.github/PULL_REQUEST_TEMPLATE.md
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
Thank you for your contribution to Abseil!
|
| 2 |
+
|
| 3 |
+
Before submitting this PR, please be sure to read our [contributing
|
| 4 |
+
guidelines](https://github.com/abseil/abseil-cpp/blob/master/CONTRIBUTING.md).
|
| 5 |
+
|
| 6 |
+
If you are a Googler, please also note that it is required that you send us a
|
| 7 |
+
Piper CL instead of using the GitHub pull-request process. The code propagation
|
| 8 |
+
process will deliver the change to GitHub.
|
weight/_dep/abseil-cpp/BUILD.bazel
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#
|
| 2 |
+
# Copyright 2020 The Abseil Authors.
|
| 3 |
+
#
|
| 4 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
| 5 |
+
# you may not use this file except in compliance with the License.
|
| 6 |
+
# You may obtain a copy of the License at
|
| 7 |
+
#
|
| 8 |
+
# https://www.apache.org/licenses/LICENSE-2.0
|
| 9 |
+
#
|
| 10 |
+
# Unless required by applicable law or agreed to in writing, software
|
| 11 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
| 12 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 13 |
+
# See the License for the specific language governing permissions and
|
| 14 |
+
# limitations under the License.
|
| 15 |
+
#
|
| 16 |
+
|
| 17 |
+
package(default_visibility = ["//visibility:public"])
|
| 18 |
+
|
| 19 |
+
licenses(["notice"]) # Apache 2.0
|
| 20 |
+
|
| 21 |
+
# Expose license for external usage through bazel.
|
| 22 |
+
exports_files([
|
| 23 |
+
"AUTHORS",
|
| 24 |
+
"LICENSE",
|
| 25 |
+
])
|
weight/_dep/abseil-cpp/CMake/Googletest/CMakeLists.txt.in
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
cmake_minimum_required(VERSION 3.10)
|
| 2 |
+
|
| 3 |
+
project(googletest-external NONE)
|
| 4 |
+
|
| 5 |
+
include(ExternalProject)
|
| 6 |
+
ExternalProject_Add(googletest
|
| 7 |
+
URL "${absl_gtest_download_url}" # May be empty
|
| 8 |
+
SOURCE_DIR "${absl_gtest_src_dir}"
|
| 9 |
+
BINARY_DIR "${absl_gtest_build_dir}"
|
| 10 |
+
CONFIGURE_COMMAND ""
|
| 11 |
+
BUILD_COMMAND ""
|
| 12 |
+
INSTALL_COMMAND ""
|
| 13 |
+
TEST_COMMAND ""
|
| 14 |
+
)
|
weight/_dep/abseil-cpp/CMake/Googletest/DownloadGTest.cmake
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Integrates googletest at configure time. Based on the instructions at
|
| 2 |
+
# https://github.com/google/googletest/tree/master/googletest#incorporating-into-an-existing-cmake-project
|
| 3 |
+
|
| 4 |
+
# Set up the external googletest project, downloading the latest from Github
|
| 5 |
+
# master if requested.
|
| 6 |
+
configure_file(
|
| 7 |
+
${CMAKE_CURRENT_LIST_DIR}/CMakeLists.txt.in
|
| 8 |
+
${CMAKE_BINARY_DIR}/googletest-external/CMakeLists.txt
|
| 9 |
+
)
|
| 10 |
+
|
| 11 |
+
set(ABSL_SAVE_CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS})
|
| 12 |
+
set(ABSL_SAVE_CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY})
|
| 13 |
+
if (BUILD_SHARED_LIBS)
|
| 14 |
+
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
|
| 15 |
+
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DGTEST_CREATE_SHARED_LIBRARY=1")
|
| 16 |
+
endif()
|
| 17 |
+
|
| 18 |
+
# Configure and build the googletest source.
|
| 19 |
+
execute_process(COMMAND ${CMAKE_COMMAND} -G "${CMAKE_GENERATOR}" .
|
| 20 |
+
RESULT_VARIABLE result
|
| 21 |
+
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/googletest-external )
|
| 22 |
+
if(result)
|
| 23 |
+
message(FATAL_ERROR "CMake step for googletest failed: ${result}")
|
| 24 |
+
endif()
|
| 25 |
+
|
| 26 |
+
execute_process(COMMAND ${CMAKE_COMMAND} --build .
|
| 27 |
+
RESULT_VARIABLE result
|
| 28 |
+
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/googletest-external)
|
| 29 |
+
if(result)
|
| 30 |
+
message(FATAL_ERROR "Build step for googletest failed: ${result}")
|
| 31 |
+
endif()
|
| 32 |
+
|
| 33 |
+
set(CMAKE_CXX_FLAGS ${ABSL_SAVE_CMAKE_CXX_FLAGS})
|
| 34 |
+
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${ABSL_SAVE_CMAKE_RUNTIME_OUTPUT_DIRECTORY})
|
| 35 |
+
|
| 36 |
+
# Prevent overriding the parent project's compiler/linker settings on Windows
|
| 37 |
+
set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
|
| 38 |
+
|
| 39 |
+
# Add googletest directly to our build. This defines the gtest and gtest_main
|
| 40 |
+
# targets.
|
| 41 |
+
add_subdirectory(${absl_gtest_src_dir} ${absl_gtest_build_dir} EXCLUDE_FROM_ALL)
|
weight/_dep/abseil-cpp/CMake/README.md
ADDED
|
@@ -0,0 +1,188 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Abseil CMake Build Instructions
|
| 2 |
+
|
| 3 |
+
Abseil comes with a CMake build script ([CMakeLists.txt](../CMakeLists.txt))
|
| 4 |
+
that can be used on a wide range of platforms ("C" stands for cross-platform.).
|
| 5 |
+
If you don't have CMake installed already, you can download it for free from
|
| 6 |
+
<https://www.cmake.org/>.
|
| 7 |
+
|
| 8 |
+
CMake works by generating native makefiles or build projects that can
|
| 9 |
+
be used in the compiler environment of your choice.
|
| 10 |
+
|
| 11 |
+
For API/ABI compatibility reasons, we strongly recommend building Abseil in a
|
| 12 |
+
subdirectory of your project or as an embedded dependency.
|
| 13 |
+
|
| 14 |
+
## Incorporating Abseil Into a CMake Project
|
| 15 |
+
|
| 16 |
+
The recommendations below are similar to those for using CMake within the
|
| 17 |
+
googletest framework
|
| 18 |
+
(<https://github.com/google/googletest/blob/master/googletest/README.md#incorporating-into-an-existing-cmake-project>)
|
| 19 |
+
|
| 20 |
+
### Step-by-Step Instructions
|
| 21 |
+
|
| 22 |
+
1. If you want to build the Abseil tests, integrate the Abseil dependency
|
| 23 |
+
[Google Test](https://github.com/google/googletest) into your CMake
|
| 24 |
+
project. To disable Abseil tests, you have to pass either
|
| 25 |
+
`-DBUILD_TESTING=OFF` or `-DABSL_BUILD_TESTING=OFF` when configuring your
|
| 26 |
+
project with CMake.
|
| 27 |
+
|
| 28 |
+
2. Download Abseil and copy it into a subdirectory in your CMake project or add
|
| 29 |
+
Abseil as a [git submodule](https://git-scm.com/docs/git-submodule) in your
|
| 30 |
+
CMake project.
|
| 31 |
+
|
| 32 |
+
3. You can then use the CMake command
|
| 33 |
+
[`add_subdirectory()`](https://cmake.org/cmake/help/latest/command/add_subdirectory.html)
|
| 34 |
+
to include Abseil directly in your CMake project.
|
| 35 |
+
|
| 36 |
+
4. Add the **absl::** target you wish to use to the
|
| 37 |
+
[`target_link_libraries()`](https://cmake.org/cmake/help/latest/command/target_link_libraries.html)
|
| 38 |
+
section of your executable or of your library.<br>
|
| 39 |
+
Here is a short CMakeLists.txt example of an application project using Abseil.
|
| 40 |
+
|
| 41 |
+
```cmake
|
| 42 |
+
cmake_minimum_required(VERSION 3.10)
|
| 43 |
+
project(my_app_project)
|
| 44 |
+
|
| 45 |
+
# Pick the C++ standard to compile with.
|
| 46 |
+
# Abseil currently supports C++14, C++17, and C++20.
|
| 47 |
+
set(CMAKE_CXX_STANDARD 14)
|
| 48 |
+
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
| 49 |
+
|
| 50 |
+
add_subdirectory(abseil-cpp)
|
| 51 |
+
|
| 52 |
+
add_executable(my_exe source.cpp)
|
| 53 |
+
target_link_libraries(my_exe absl::base absl::synchronization absl::strings)
|
| 54 |
+
```
|
| 55 |
+
|
| 56 |
+
Note that if you are developing a library designed for use by other clients, you
|
| 57 |
+
should instead leave `CMAKE_CXX_STANDARD` unset (or only set if being built as
|
| 58 |
+
the current top-level CMake project) and configure the minimum required C++
|
| 59 |
+
standard at the target level. If you require a later minimum C++ standard than
|
| 60 |
+
Abseil does, it's a good idea to also enforce that `CMAKE_CXX_STANDARD` (which
|
| 61 |
+
will control Abseil library targets) is set to at least that minimum. For
|
| 62 |
+
example:
|
| 63 |
+
|
| 64 |
+
```cmake
|
| 65 |
+
cmake_minimum_required(VERSION 3.10)
|
| 66 |
+
project(my_lib_project)
|
| 67 |
+
|
| 68 |
+
# Leave C++ standard up to the root application, so set it only if this is the
|
| 69 |
+
# current top-level CMake project.
|
| 70 |
+
if(CMAKE_SOURCE_DIR STREQUAL my_lib_project_SOURCE_DIR)
|
| 71 |
+
set(CMAKE_CXX_STANDARD 17)
|
| 72 |
+
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
| 73 |
+
endif()
|
| 74 |
+
|
| 75 |
+
add_subdirectory(abseil-cpp)
|
| 76 |
+
|
| 77 |
+
add_library(my_lib source.cpp)
|
| 78 |
+
target_link_libraries(my_lib absl::base absl::synchronization absl::strings)
|
| 79 |
+
|
| 80 |
+
# Enforce that my_lib requires C++17. Important to document for clients that they
|
| 81 |
+
# must set CMAKE_CXX_STANDARD to 17 or higher for proper Abseil ABI compatibility
|
| 82 |
+
# (since otherwise, Abseil library targets could be compiled with a lower C++
|
| 83 |
+
# standard than my_lib).
|
| 84 |
+
target_compile_features(my_lib PUBLIC cxx_std_17)
|
| 85 |
+
if(CMAKE_CXX_STANDARD LESS 17)
|
| 86 |
+
message(FATAL_ERROR
|
| 87 |
+
"my_lib_project requires CMAKE_CXX_STANDARD >= 17 (got: ${CMAKE_CXX_STANDARD})")
|
| 88 |
+
endif()
|
| 89 |
+
```
|
| 90 |
+
|
| 91 |
+
Then the top-level application project that uses your library is responsible for
|
| 92 |
+
setting a consistent `CMAKE_CXX_STANDARD` that is sufficiently high.
|
| 93 |
+
|
| 94 |
+
### Running Abseil Tests with CMake
|
| 95 |
+
|
| 96 |
+
Use the `-DABSL_BUILD_TESTING=ON` flag to run Abseil tests. Note that
|
| 97 |
+
BUILD_TESTING must also be on (the default).
|
| 98 |
+
|
| 99 |
+
You will need to provide Abseil with a Googletest dependency. There are two
|
| 100 |
+
options for how to do this:
|
| 101 |
+
|
| 102 |
+
* Use `-DABSL_USE_GOOGLETEST_HEAD`. This will automatically download the latest
|
| 103 |
+
Googletest source into the build directory at configure time. Googletest will
|
| 104 |
+
then be compiled directly alongside Abseil's tests.
|
| 105 |
+
* Manually integrate Googletest with your build. See
|
| 106 |
+
https://github.com/google/googletest/blob/master/googletest/README.md#using-cmake
|
| 107 |
+
for more information on using Googletest in a CMake project.
|
| 108 |
+
|
| 109 |
+
For example, to run just the Abseil tests, you could use this script:
|
| 110 |
+
|
| 111 |
+
```
|
| 112 |
+
cd path/to/abseil-cpp
|
| 113 |
+
mkdir build
|
| 114 |
+
cd build
|
| 115 |
+
cmake -DABSL_BUILD_TESTING=ON -DABSL_USE_GOOGLETEST_HEAD=ON ..
|
| 116 |
+
make -j
|
| 117 |
+
ctest
|
| 118 |
+
```
|
| 119 |
+
|
| 120 |
+
Currently, we only run our tests with CMake in a Linux environment, but we are
|
| 121 |
+
working on the rest of our supported platforms. See
|
| 122 |
+
https://github.com/abseil/abseil-cpp/projects/1 and
|
| 123 |
+
https://github.com/abseil/abseil-cpp/issues/109 for more information.
|
| 124 |
+
|
| 125 |
+
### Available Abseil CMake Public Targets
|
| 126 |
+
|
| 127 |
+
Here's a non-exhaustive list of Abseil CMake public targets:
|
| 128 |
+
|
| 129 |
+
```cmake
|
| 130 |
+
absl::algorithm
|
| 131 |
+
absl::base
|
| 132 |
+
absl::debugging
|
| 133 |
+
absl::flat_hash_map
|
| 134 |
+
absl::flags
|
| 135 |
+
absl::memory
|
| 136 |
+
absl::meta
|
| 137 |
+
absl::numeric
|
| 138 |
+
absl::random_random
|
| 139 |
+
absl::strings
|
| 140 |
+
absl::synchronization
|
| 141 |
+
absl::time
|
| 142 |
+
absl::utility
|
| 143 |
+
```
|
| 144 |
+
|
| 145 |
+
## Traditional CMake Set-Up
|
| 146 |
+
|
| 147 |
+
For larger projects, it may make sense to use the traditional CMake set-up where you build and install projects separately.
|
| 148 |
+
|
| 149 |
+
First, you'd need to build and install Google Test:
|
| 150 |
+
```
|
| 151 |
+
cmake -S /source/googletest -B /build/googletest -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/installation/dir -DBUILD_GMOCK=ON
|
| 152 |
+
cmake --build /build/googletest --target install
|
| 153 |
+
```
|
| 154 |
+
|
| 155 |
+
Then you need to configure and build Abseil. Make sure you enable `ABSL_USE_EXTERNAL_GOOGLETEST` and `ABSL_FIND_GOOGLETEST`. You also need to enable `ABSL_ENABLE_INSTALL` so that you can install Abseil itself.
|
| 156 |
+
```
|
| 157 |
+
cmake -S /source/abseil-cpp -B /build/abseil-cpp -DCMAKE_PREFIX_PATH=/installation/dir -DCMAKE_INSTALL_PREFIX=/installation/dir -DABSL_ENABLE_INSTALL=ON -DABSL_USE_EXTERNAL_GOOGLETEST=ON -DABSL_FIND_GOOGLETEST=ON
|
| 158 |
+
cmake --build /temporary/build/abseil-cpp
|
| 159 |
+
```
|
| 160 |
+
|
| 161 |
+
(`CMAKE_PREFIX_PATH` is where you already have Google Test installed; `CMAKE_INSTALL_PREFIX` is where you want to have Abseil installed; they can be different.)
|
| 162 |
+
|
| 163 |
+
Run the tests:
|
| 164 |
+
```
|
| 165 |
+
ctest --test-dir /temporary/build/abseil-cpp
|
| 166 |
+
```
|
| 167 |
+
|
| 168 |
+
And finally install:
|
| 169 |
+
```
|
| 170 |
+
cmake --build /temporary/build/abseil-cpp --target install
|
| 171 |
+
```
|
| 172 |
+
|
| 173 |
+
# CMake Option Synopsis
|
| 174 |
+
|
| 175 |
+
## Enable Standard CMake Installation
|
| 176 |
+
|
| 177 |
+
`-DABSL_ENABLE_INSTALL=ON`
|
| 178 |
+
|
| 179 |
+
## Google Test Options
|
| 180 |
+
|
| 181 |
+
`-DABSL_BUILD_TESTING=ON` must be set to enable testing
|
| 182 |
+
|
| 183 |
+
- Have Abseil download and build Google Test for you: `-DABSL_USE_EXTERNAL_GOOGLETEST=OFF` (default)
|
| 184 |
+
- Download and build latest Google Test: `-DABSL_USE_GOOGLETEST_HEAD=ON`
|
| 185 |
+
- Download specific Google Test version (ZIP archive): `-DABSL_GOOGLETEST_DOWNLOAD_URL=https://.../version.zip`
|
| 186 |
+
- Use Google Test from specific local directory: `-DABSL_LOCAL_GOOGLETEST_DIR=/path/to/googletest`
|
| 187 |
+
- Use Google Test included elsewhere in your project: `-DABSL_USE_EXTERNAL_GOOGLETEST=ON`
|
| 188 |
+
- Use standard CMake `find_package(CTest)` to find installed Google Test: `-DABSL_USE_EXTERNAL_GOOGLETEST=ON -DABSL_FIND_GOOGLETEST=ON`
|
weight/_dep/abseil-cpp/CMake/install_test_project/CMakeLists.txt
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#
|
| 2 |
+
# Copyright 2019 The Abseil Authors.
|
| 3 |
+
#
|
| 4 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
| 5 |
+
# you may not use this file except in compliance with the License.
|
| 6 |
+
# You may obtain a copy of the License at
|
| 7 |
+
#
|
| 8 |
+
# https://www.apache.org/licenses/LICENSE-2.0
|
| 9 |
+
#
|
| 10 |
+
# Unless required by applicable law or agreed to in writing, software
|
| 11 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
| 12 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 13 |
+
# See the License for the specific language governing permissions and
|
| 14 |
+
# limitations under the License.
|
| 15 |
+
|
| 16 |
+
# A simple CMakeLists.txt for testing cmake installation
|
| 17 |
+
|
| 18 |
+
cmake_minimum_required(VERSION 3.10)
|
| 19 |
+
project(absl_cmake_testing CXX)
|
| 20 |
+
|
| 21 |
+
add_executable(simple simple.cc)
|
| 22 |
+
|
| 23 |
+
find_package(absl REQUIRED)
|
| 24 |
+
|
| 25 |
+
target_link_libraries(simple absl::strings absl::config)
|
weight/_dep/abseil-cpp/CMake/install_test_project/simple.cc
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
//
|
| 2 |
+
// Copyright 2019 The Abseil Authors.
|
| 3 |
+
//
|
| 4 |
+
// Licensed under the Apache License, Version 2.0 (the "License");
|
| 5 |
+
// you may not use this file except in compliance with the License.
|
| 6 |
+
// You may obtain a copy of the License at
|
| 7 |
+
//
|
| 8 |
+
// https://www.apache.org/licenses/LICENSE-2.0
|
| 9 |
+
//
|
| 10 |
+
// Unless required by applicable law or agreed to in writing, software
|
| 11 |
+
// distributed under the License is distributed on an "AS IS" BASIS,
|
| 12 |
+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 13 |
+
// See the License for the specific language governing permissions and
|
| 14 |
+
// limitations under the License.
|
| 15 |
+
|
| 16 |
+
#include <iostream>
|
| 17 |
+
#include "absl/base/config.h"
|
| 18 |
+
#include "absl/strings/substitute.h"
|
| 19 |
+
|
| 20 |
+
#if !defined(ABSL_LTS_RELEASE_VERSION) || ABSL_LTS_RELEASE_VERSION != 99998877
|
| 21 |
+
#error ABSL_LTS_RELEASE_VERSION is not set correctly.
|
| 22 |
+
#endif
|
| 23 |
+
|
| 24 |
+
#if !defined(ABSL_LTS_RELEASE_PATCH_LEVEL) || ABSL_LTS_RELEASE_PATCH_LEVEL != 0
|
| 25 |
+
#error ABSL_LTS_RELEASE_PATCH_LEVEL is not set correctly.
|
| 26 |
+
#endif
|
| 27 |
+
|
| 28 |
+
int main(int argc, char** argv) {
|
| 29 |
+
for (int i = 0; i < argc; ++i) {
|
| 30 |
+
std::cout << absl::Substitute("Arg $0: $1\n", i, argv[i]);
|
| 31 |
+
}
|
| 32 |
+
}
|
weight/_dep/abseil-cpp/CMake/install_test_project/test.sh
ADDED
|
@@ -0,0 +1,112 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/bin/bash
|
| 2 |
+
#
|
| 3 |
+
# Copyright 2019 The Abseil Authors.
|
| 4 |
+
#
|
| 5 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
| 6 |
+
# you may not use this file except in compliance with the License.
|
| 7 |
+
# You may obtain a copy of the License at
|
| 8 |
+
#
|
| 9 |
+
# https://www.apache.org/licenses/LICENSE-2.0
|
| 10 |
+
#
|
| 11 |
+
# Unless required by applicable law or agreed to in writing, software
|
| 12 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
| 13 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 14 |
+
# See the License for the specific language governing permissions and
|
| 15 |
+
# limitations under the License.
|
| 16 |
+
#
|
| 17 |
+
# Unit and integration tests for Abseil LTS CMake installation
|
| 18 |
+
|
| 19 |
+
# Fail on any error. Treat unset variables an error. Print commands as executed.
|
| 20 |
+
set -euox pipefail
|
| 21 |
+
|
| 22 |
+
absl_dir=/abseil-cpp
|
| 23 |
+
absl_build_dir=/buildfs
|
| 24 |
+
googletest_builddir=/googletest_builddir
|
| 25 |
+
project_dir="${absl_dir}"/CMake/install_test_project
|
| 26 |
+
project_build_dir=/buildfs/project-build
|
| 27 |
+
|
| 28 |
+
build_shared_libs="OFF"
|
| 29 |
+
if [ "${LINK_TYPE:-}" = "DYNAMIC" ]; then
|
| 30 |
+
build_shared_libs="ON"
|
| 31 |
+
fi
|
| 32 |
+
|
| 33 |
+
# Build and install GoogleTest
|
| 34 |
+
mkdir "${googletest_builddir}"
|
| 35 |
+
pushd "${googletest_builddir}"
|
| 36 |
+
curl -L "${ABSL_GOOGLETEST_DOWNLOAD_URL}" --output "${ABSL_GOOGLETEST_COMMIT}".zip
|
| 37 |
+
unzip "${ABSL_GOOGLETEST_COMMIT}".zip
|
| 38 |
+
pushd "googletest-${ABSL_GOOGLETEST_COMMIT}"
|
| 39 |
+
mkdir build
|
| 40 |
+
pushd build
|
| 41 |
+
cmake -DCMAKE_BUILD_TYPE=Release -DBUILD_SHARED_LIBS="${build_shared_libs}" ..
|
| 42 |
+
make -j $(nproc)
|
| 43 |
+
make install
|
| 44 |
+
ldconfig
|
| 45 |
+
popd
|
| 46 |
+
popd
|
| 47 |
+
popd
|
| 48 |
+
|
| 49 |
+
# Run the LTS transformations
|
| 50 |
+
./create_lts.py 99998877
|
| 51 |
+
|
| 52 |
+
# Build and install Abseil
|
| 53 |
+
pushd "${absl_build_dir}"
|
| 54 |
+
cmake "${absl_dir}" \
|
| 55 |
+
-DABSL_USE_EXTERNAL_GOOGLETEST=ON \
|
| 56 |
+
-DABSL_FIND_GOOGLETEST=ON \
|
| 57 |
+
-DCMAKE_BUILD_TYPE=Release \
|
| 58 |
+
-DABSL_BUILD_TESTING=ON \
|
| 59 |
+
-DBUILD_SHARED_LIBS="${build_shared_libs}"
|
| 60 |
+
make -j $(nproc)
|
| 61 |
+
ctest -j $(nproc) --output-on-failure
|
| 62 |
+
make install
|
| 63 |
+
ldconfig
|
| 64 |
+
popd
|
| 65 |
+
|
| 66 |
+
# Test the project against the installed Abseil
|
| 67 |
+
mkdir -p "${project_build_dir}"
|
| 68 |
+
pushd "${project_build_dir}"
|
| 69 |
+
cmake "${project_dir}"
|
| 70 |
+
cmake --build . --target simple
|
| 71 |
+
|
| 72 |
+
output="$(${project_build_dir}/simple "printme" 2>&1)"
|
| 73 |
+
if [[ "${output}" != *"Arg 1: printme"* ]]; then
|
| 74 |
+
echo "Faulty output on simple project:"
|
| 75 |
+
echo "${output}"
|
| 76 |
+
exit 1
|
| 77 |
+
fi
|
| 78 |
+
|
| 79 |
+
popd
|
| 80 |
+
|
| 81 |
+
if ! grep absl::strings "/usr/local/lib/cmake/absl/abslTargets.cmake"; then
|
| 82 |
+
cat "/usr/local/lib/cmake/absl/abslTargets.cmake"
|
| 83 |
+
echo "CMake targets named incorrectly"
|
| 84 |
+
exit 1
|
| 85 |
+
fi
|
| 86 |
+
|
| 87 |
+
pushd "${HOME}"
|
| 88 |
+
cat > hello-abseil.cc << EOF
|
| 89 |
+
#include <cstdlib>
|
| 90 |
+
|
| 91 |
+
#include "absl/strings/str_format.h"
|
| 92 |
+
|
| 93 |
+
int main(int argc, char **argv) {
|
| 94 |
+
absl::PrintF("Hello Abseil!\n");
|
| 95 |
+
return EXIT_SUCCESS;
|
| 96 |
+
}
|
| 97 |
+
EOF
|
| 98 |
+
|
| 99 |
+
if [ "${LINK_TYPE:-}" != "DYNAMIC" ]; then
|
| 100 |
+
pc_args=($(pkg-config --cflags --libs --static absl_str_format))
|
| 101 |
+
g++ -static -o hello-abseil hello-abseil.cc "${pc_args[@]}"
|
| 102 |
+
else
|
| 103 |
+
pc_args=($(pkg-config --cflags --libs absl_str_format))
|
| 104 |
+
g++ -o hello-abseil hello-abseil.cc "${pc_args[@]}"
|
| 105 |
+
fi
|
| 106 |
+
hello="$(./hello-abseil)"
|
| 107 |
+
[[ "${hello}" == "Hello Abseil!" ]]
|
| 108 |
+
|
| 109 |
+
popd
|
| 110 |
+
|
| 111 |
+
echo "Install test complete!"
|
| 112 |
+
exit 0
|
weight/_dep/abseil-cpp/CMakeLists.txt
ADDED
|
@@ -0,0 +1,270 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#
|
| 2 |
+
# Copyright 2017 The Abseil Authors.
|
| 3 |
+
#
|
| 4 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
| 5 |
+
# you may not use this file except in compliance with the License.
|
| 6 |
+
# You may obtain a copy of the License at
|
| 7 |
+
#
|
| 8 |
+
# https://www.apache.org/licenses/LICENSE-2.0
|
| 9 |
+
#
|
| 10 |
+
# Unless required by applicable law or agreed to in writing, software
|
| 11 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
| 12 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 13 |
+
# See the License for the specific language governing permissions and
|
| 14 |
+
# limitations under the License.
|
| 15 |
+
#
|
| 16 |
+
|
| 17 |
+
# https://github.com/google/oss-policies-info/blob/main/foundational-cxx-support-matrix.md
|
| 18 |
+
# As of 2022-09-06, CMake 3.10 is the minimum supported version.
|
| 19 |
+
cmake_minimum_required(VERSION 3.10)
|
| 20 |
+
|
| 21 |
+
# Compiler id for Apple Clang is now AppleClang.
|
| 22 |
+
if (POLICY CMP0025)
|
| 23 |
+
cmake_policy(SET CMP0025 NEW)
|
| 24 |
+
endif (POLICY CMP0025)
|
| 25 |
+
|
| 26 |
+
# if command can use IN_LIST
|
| 27 |
+
if (POLICY CMP0057)
|
| 28 |
+
cmake_policy(SET CMP0057 NEW)
|
| 29 |
+
endif (POLICY CMP0057)
|
| 30 |
+
|
| 31 |
+
# Project version variables are the empty string if version is unspecified
|
| 32 |
+
if (POLICY CMP0048)
|
| 33 |
+
cmake_policy(SET CMP0048 NEW)
|
| 34 |
+
endif (POLICY CMP0048)
|
| 35 |
+
|
| 36 |
+
# Honor the GTest_ROOT variable if specified
|
| 37 |
+
if (POLICY CMP0074)
|
| 38 |
+
cmake_policy(SET CMP0074 NEW)
|
| 39 |
+
endif (POLICY CMP0074)
|
| 40 |
+
|
| 41 |
+
# option() honor variables
|
| 42 |
+
if (POLICY CMP0077)
|
| 43 |
+
cmake_policy(SET CMP0077 NEW)
|
| 44 |
+
endif (POLICY CMP0077)
|
| 45 |
+
|
| 46 |
+
# Allow the user to specify the MSVC runtime
|
| 47 |
+
if (POLICY CMP0091)
|
| 48 |
+
cmake_policy(SET CMP0091 NEW)
|
| 49 |
+
endif (POLICY CMP0091)
|
| 50 |
+
|
| 51 |
+
# try_compile() honors the CMAKE_CXX_STANDARD value
|
| 52 |
+
if (POLICY CMP0067)
|
| 53 |
+
cmake_policy(SET CMP0067 NEW)
|
| 54 |
+
endif (POLICY CMP0067)
|
| 55 |
+
|
| 56 |
+
# Allow the user to specify the CMAKE_MSVC_DEBUG_INFORMATION_FORMAT
|
| 57 |
+
if (POLICY CMP0141)
|
| 58 |
+
cmake_policy(SET CMP0141 NEW)
|
| 59 |
+
endif (POLICY CMP0141)
|
| 60 |
+
|
| 61 |
+
project(absl LANGUAGES CXX VERSION 20240116)
|
| 62 |
+
include(CTest)
|
| 63 |
+
|
| 64 |
+
# Output directory is correct by default for most build setups. However, when
|
| 65 |
+
# building Abseil as a DLL, it is important to have the DLL in the same
|
| 66 |
+
# directory as the executable using it. Thus, we put all executables in a single
|
| 67 |
+
# /bin directory.
|
| 68 |
+
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
|
| 69 |
+
|
| 70 |
+
# when absl is included as subproject (i.e. using add_subdirectory(abseil-cpp))
|
| 71 |
+
# in the source tree of a project that uses it, install rules are disabled.
|
| 72 |
+
if(NOT CMAKE_SOURCE_DIR STREQUAL PROJECT_SOURCE_DIR)
|
| 73 |
+
option(ABSL_ENABLE_INSTALL "Enable install rule" OFF)
|
| 74 |
+
else()
|
| 75 |
+
option(ABSL_ENABLE_INSTALL "Enable install rule" ON)
|
| 76 |
+
endif()
|
| 77 |
+
|
| 78 |
+
option(ABSL_PROPAGATE_CXX_STD
|
| 79 |
+
"Use CMake C++ standard meta features (e.g. cxx_std_14) that propagate to targets that link to Abseil"
|
| 80 |
+
OFF) # TODO: Default to ON for CMake 3.8 and greater.
|
| 81 |
+
if(NOT ABSL_PROPAGATE_CXX_STD)
|
| 82 |
+
message(WARNING "A future Abseil release will default ABSL_PROPAGATE_CXX_STD to ON for CMake 3.8 and up. We recommend enabling this option to ensure your project still builds correctly.")
|
| 83 |
+
endif()
|
| 84 |
+
|
| 85 |
+
option(ABSL_USE_SYSTEM_INCLUDES
|
| 86 |
+
"Silence warnings in Abseil headers by marking them as SYSTEM includes"
|
| 87 |
+
OFF)
|
| 88 |
+
|
| 89 |
+
list(APPEND CMAKE_MODULE_PATH
|
| 90 |
+
${CMAKE_CURRENT_LIST_DIR}/CMake
|
| 91 |
+
${CMAKE_CURRENT_LIST_DIR}/absl/copts
|
| 92 |
+
)
|
| 93 |
+
|
| 94 |
+
include(CMakePackageConfigHelpers)
|
| 95 |
+
include(GNUInstallDirs)
|
| 96 |
+
include(AbseilDll)
|
| 97 |
+
include(AbseilHelpers)
|
| 98 |
+
|
| 99 |
+
|
| 100 |
+
##
|
| 101 |
+
## Using absl targets
|
| 102 |
+
##
|
| 103 |
+
## all public absl targets are
|
| 104 |
+
## exported with the absl:: prefix
|
| 105 |
+
##
|
| 106 |
+
## e.g absl::base absl::synchronization absl::strings ....
|
| 107 |
+
##
|
| 108 |
+
## DO NOT rely on the internal targets outside of the prefix
|
| 109 |
+
|
| 110 |
+
|
| 111 |
+
# include current path
|
| 112 |
+
list(APPEND ABSL_COMMON_INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR})
|
| 113 |
+
|
| 114 |
+
if("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang")
|
| 115 |
+
set(ABSL_USING_CLANG ON)
|
| 116 |
+
else()
|
| 117 |
+
set(ABSL_USING_CLANG OFF)
|
| 118 |
+
endif()
|
| 119 |
+
|
| 120 |
+
# find dependencies
|
| 121 |
+
## pthread
|
| 122 |
+
find_package(Threads REQUIRED)
|
| 123 |
+
|
| 124 |
+
include(CMakeDependentOption)
|
| 125 |
+
|
| 126 |
+
option(ABSL_BUILD_TESTING
|
| 127 |
+
"If ON, Abseil will build all of Abseil's own tests." OFF)
|
| 128 |
+
|
| 129 |
+
option(ABSL_BUILD_TEST_HELPERS
|
| 130 |
+
"If ON, Abseil will build libraries that you can use to write tests against Abseil code. This option requires that Abseil is configured to use GoogleTest."
|
| 131 |
+
OFF)
|
| 132 |
+
|
| 133 |
+
option(ABSL_USE_EXTERNAL_GOOGLETEST
|
| 134 |
+
"If ON, Abseil will assume that the targets for GoogleTest are already provided by the including project. This makes sense when Abseil is used with add_subdirectory." OFF)
|
| 135 |
+
|
| 136 |
+
cmake_dependent_option(ABSL_FIND_GOOGLETEST
|
| 137 |
+
"If ON, Abseil will use find_package(GTest) rather than assuming that GoogleTest is already provided by the including project."
|
| 138 |
+
ON
|
| 139 |
+
"ABSL_USE_EXTERNAL_GOOGLETEST"
|
| 140 |
+
OFF)
|
| 141 |
+
|
| 142 |
+
|
| 143 |
+
option(ABSL_USE_GOOGLETEST_HEAD
|
| 144 |
+
"If ON, abseil will download HEAD from GoogleTest at config time." OFF)
|
| 145 |
+
|
| 146 |
+
set(ABSL_GOOGLETEST_DOWNLOAD_URL "" CACHE STRING "If set, download GoogleTest from this URL")
|
| 147 |
+
|
| 148 |
+
set(ABSL_LOCAL_GOOGLETEST_DIR "/usr/src/googletest" CACHE PATH
|
| 149 |
+
"If ABSL_USE_GOOGLETEST_HEAD is OFF and ABSL_GOOGLETEST_URL is not set, specifies the directory of a local GoogleTest checkout."
|
| 150 |
+
)
|
| 151 |
+
|
| 152 |
+
if((BUILD_TESTING AND ABSL_BUILD_TESTING) OR ABSL_BUILD_TEST_HELPERS)
|
| 153 |
+
if (ABSL_USE_EXTERNAL_GOOGLETEST)
|
| 154 |
+
if (ABSL_FIND_GOOGLETEST)
|
| 155 |
+
find_package(GTest REQUIRED)
|
| 156 |
+
elseif(NOT TARGET GTest::gtest)
|
| 157 |
+
if(TARGET gtest)
|
| 158 |
+
# When Google Test is included directly rather than through find_package, the aliases are missing.
|
| 159 |
+
add_library(GTest::gtest ALIAS gtest)
|
| 160 |
+
add_library(GTest::gtest_main ALIAS gtest_main)
|
| 161 |
+
add_library(GTest::gmock ALIAS gmock)
|
| 162 |
+
add_library(GTest::gmock_main ALIAS gmock_main)
|
| 163 |
+
else()
|
| 164 |
+
message(FATAL_ERROR "ABSL_USE_EXTERNAL_GOOGLETEST is ON and ABSL_FIND_GOOGLETEST is OFF, which means that the top-level project must build the Google Test project. However, the target gtest was not found.")
|
| 165 |
+
endif()
|
| 166 |
+
endif()
|
| 167 |
+
else()
|
| 168 |
+
set(absl_gtest_build_dir ${CMAKE_BINARY_DIR}/googletest-build)
|
| 169 |
+
if(ABSL_USE_GOOGLETEST_HEAD AND ABSL_GOOGLETEST_DOWNLOAD_URL)
|
| 170 |
+
message(FATAL_ERROR "Do not set both ABSL_USE_GOOGLETEST_HEAD and ABSL_GOOGLETEST_DOWNLOAD_URL")
|
| 171 |
+
endif()
|
| 172 |
+
if(ABSL_USE_GOOGLETEST_HEAD)
|
| 173 |
+
set(absl_gtest_download_url "https://github.com/google/googletest/archive/main.zip")
|
| 174 |
+
elseif(ABSL_GOOGLETEST_DOWNLOAD_URL)
|
| 175 |
+
set(absl_gtest_download_url ${ABSL_GOOGLETEST_DOWNLOAD_URL})
|
| 176 |
+
endif()
|
| 177 |
+
if(absl_gtest_download_url)
|
| 178 |
+
set(absl_gtest_src_dir ${CMAKE_BINARY_DIR}/googletest-src)
|
| 179 |
+
else()
|
| 180 |
+
set(absl_gtest_src_dir ${ABSL_LOCAL_GOOGLETEST_DIR})
|
| 181 |
+
endif()
|
| 182 |
+
include(CMake/Googletest/DownloadGTest.cmake)
|
| 183 |
+
endif()
|
| 184 |
+
endif()
|
| 185 |
+
|
| 186 |
+
add_subdirectory(absl)
|
| 187 |
+
|
| 188 |
+
if(ABSL_ENABLE_INSTALL)
|
| 189 |
+
|
| 190 |
+
|
| 191 |
+
# install as a subdirectory only
|
| 192 |
+
install(EXPORT ${PROJECT_NAME}Targets
|
| 193 |
+
NAMESPACE absl::
|
| 194 |
+
DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}"
|
| 195 |
+
)
|
| 196 |
+
|
| 197 |
+
configure_package_config_file(
|
| 198 |
+
CMake/abslConfig.cmake.in
|
| 199 |
+
"${PROJECT_BINARY_DIR}/${PROJECT_NAME}Config.cmake"
|
| 200 |
+
INSTALL_DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}"
|
| 201 |
+
)
|
| 202 |
+
install(FILES "${PROJECT_BINARY_DIR}/${PROJECT_NAME}Config.cmake"
|
| 203 |
+
DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}"
|
| 204 |
+
)
|
| 205 |
+
|
| 206 |
+
# Abseil only has a version in LTS releases. This mechanism is accomplished
|
| 207 |
+
# Abseil's internal Copybara (https://github.com/google/copybara) workflows and
|
| 208 |
+
# isn't visible in the CMake buildsystem itself.
|
| 209 |
+
if(absl_VERSION)
|
| 210 |
+
write_basic_package_version_file(
|
| 211 |
+
"${PROJECT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake"
|
| 212 |
+
COMPATIBILITY ExactVersion
|
| 213 |
+
)
|
| 214 |
+
|
| 215 |
+
install(FILES "${PROJECT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake"
|
| 216 |
+
DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}"
|
| 217 |
+
)
|
| 218 |
+
endif() # absl_VERSION
|
| 219 |
+
|
| 220 |
+
install(DIRECTORY absl
|
| 221 |
+
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
|
| 222 |
+
FILES_MATCHING
|
| 223 |
+
PATTERN "*.inc"
|
| 224 |
+
PATTERN "*.h"
|
| 225 |
+
PATTERN "copts" EXCLUDE
|
| 226 |
+
PATTERN "testdata" EXCLUDE
|
| 227 |
+
)
|
| 228 |
+
|
| 229 |
+
# Rewrite options.h to use the compiled ABI.
|
| 230 |
+
file(READ "absl/base/options.h" ABSL_INTERNAL_OPTIONS_H_CONTENTS)
|
| 231 |
+
|
| 232 |
+
# Handle features that require at least C++20.
|
| 233 |
+
if (ABSL_INTERNAL_AT_LEAST_CXX20)
|
| 234 |
+
foreach(FEATURE "ORDERING")
|
| 235 |
+
string(REPLACE
|
| 236 |
+
"#define ABSL_OPTION_USE_STD_${FEATURE} 2"
|
| 237 |
+
"#define ABSL_OPTION_USE_STD_${FEATURE} 1"
|
| 238 |
+
ABSL_INTERNAL_OPTIONS_H_PINNED
|
| 239 |
+
"${ABSL_INTERNAL_OPTIONS_H_CONTENTS}")
|
| 240 |
+
set(ABSL_INTERNAL_OPTIONS_H_CONTENTS "${ABSL_INTERNAL_OPTIONS_H_PINNED}")
|
| 241 |
+
endforeach()
|
| 242 |
+
endif()
|
| 243 |
+
|
| 244 |
+
# Handle features that require at least C++17.
|
| 245 |
+
if (ABSL_INTERNAL_AT_LEAST_CXX17)
|
| 246 |
+
foreach(FEATURE "ANY" "OPTIONAL" "STRING_VIEW" "VARIANT")
|
| 247 |
+
string(REPLACE
|
| 248 |
+
"#define ABSL_OPTION_USE_STD_${FEATURE} 2"
|
| 249 |
+
"#define ABSL_OPTION_USE_STD_${FEATURE} 1"
|
| 250 |
+
ABSL_INTERNAL_OPTIONS_H_PINNED
|
| 251 |
+
"${ABSL_INTERNAL_OPTIONS_H_CONTENTS}")
|
| 252 |
+
set(ABSL_INTERNAL_OPTIONS_H_CONTENTS "${ABSL_INTERNAL_OPTIONS_H_PINNED}")
|
| 253 |
+
endforeach()
|
| 254 |
+
endif()
|
| 255 |
+
|
| 256 |
+
# Any feature that still has the value of 2 (because it was not handled above)
|
| 257 |
+
# should be set to 0.
|
| 258 |
+
string(REGEX REPLACE
|
| 259 |
+
"#define ABSL_OPTION_USE_STD_([^ ]*) 2"
|
| 260 |
+
"#define ABSL_OPTION_USE_STD_\\1 0"
|
| 261 |
+
ABSL_INTERNAL_OPTIONS_H_PINNED
|
| 262 |
+
"${ABSL_INTERNAL_OPTIONS_H_CONTENTS}")
|
| 263 |
+
|
| 264 |
+
file(WRITE "${CMAKE_BINARY_DIR}/options-pinned.h" "${ABSL_INTERNAL_OPTIONS_H_PINNED}")
|
| 265 |
+
|
| 266 |
+
install(FILES "${CMAKE_BINARY_DIR}/options-pinned.h"
|
| 267 |
+
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/absl/base
|
| 268 |
+
RENAME "options.h")
|
| 269 |
+
|
| 270 |
+
endif() # ABSL_ENABLE_INSTALL
|
weight/_dep/abseil-cpp/CONTRIBUTING.md
ADDED
|
@@ -0,0 +1,141 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# How to Contribute to Abseil
|
| 2 |
+
|
| 3 |
+
We'd love to accept your patches and contributions to this project. There are
|
| 4 |
+
just a few small guidelines you need to follow.
|
| 5 |
+
|
| 6 |
+
NOTE: If you are new to GitHub, please start by reading [Pull Request
|
| 7 |
+
howto](https://help.github.com/articles/about-pull-requests/)
|
| 8 |
+
|
| 9 |
+
## Contributor License Agreement
|
| 10 |
+
|
| 11 |
+
Contributions to this project must be accompanied by a Contributor License
|
| 12 |
+
Agreement. You (or your employer) retain the copyright to your contribution,
|
| 13 |
+
this simply gives us permission to use and redistribute your contributions as
|
| 14 |
+
part of the project. Head over to <https://cla.developers.google.com/> to see
|
| 15 |
+
your current agreements on file or to sign a new one.
|
| 16 |
+
|
| 17 |
+
You generally only need to submit a CLA once, so if you've already submitted one
|
| 18 |
+
(even if it was for a different project), you probably don't need to do it
|
| 19 |
+
again.
|
| 20 |
+
|
| 21 |
+
## Contribution Guidelines
|
| 22 |
+
|
| 23 |
+
Potential contributors sometimes ask us if the Abseil project is the appropriate
|
| 24 |
+
home for their utility library code or for specific functions implementing
|
| 25 |
+
missing portions of the standard. Often, the answer to this question is "no".
|
| 26 |
+
We’d like to articulate our thinking on this issue so that our choices can be
|
| 27 |
+
understood by everyone and so that contributors can have a better intuition
|
| 28 |
+
about whether Abseil might be interested in adopting a new library.
|
| 29 |
+
|
| 30 |
+
### Priorities
|
| 31 |
+
|
| 32 |
+
Although our mission is to augment the C++ standard library, our goal is not to
|
| 33 |
+
provide a full forward-compatible implementation of the latest standard. For us
|
| 34 |
+
to consider a library for inclusion in Abseil, it is not enough that a library
|
| 35 |
+
is useful. We generally choose to release a library when it meets at least one
|
| 36 |
+
of the following criteria:
|
| 37 |
+
|
| 38 |
+
* **Widespread usage** - Using our internal codebase to help gauge usage, most
|
| 39 |
+
of the libraries we've released have tens of thousands of users.
|
| 40 |
+
* **Anticipated widespread usage** - Pre-adoption of some standard-compliant
|
| 41 |
+
APIs may not have broad adoption initially but can be expected to pick up
|
| 42 |
+
usage when it replaces legacy APIs. `absl::from_chars`, for example,
|
| 43 |
+
replaces existing code that converts strings to numbers and will therefore
|
| 44 |
+
likely see usage growth.
|
| 45 |
+
* **High impact** - APIs that provide a key solution to a specific problem,
|
| 46 |
+
such as `absl::FixedArray`, have higher impact than usage numbers may signal
|
| 47 |
+
and are released because of their importance.
|
| 48 |
+
* **Direct support for a library that falls under one of the above** - When we
|
| 49 |
+
want access to a smaller library as an implementation detail for a
|
| 50 |
+
higher-priority library we plan to release, we may release it, as we did
|
| 51 |
+
with portions of `absl/meta/type_traits.h`. One consequence of this is that
|
| 52 |
+
the presence of a library in Abseil does not necessarily mean that other
|
| 53 |
+
similar libraries would be a high priority.
|
| 54 |
+
|
| 55 |
+
### API Freeze Consequences
|
| 56 |
+
|
| 57 |
+
Via the
|
| 58 |
+
[Abseil Compatibility Guidelines](https://abseil.io/about/compatibility), we
|
| 59 |
+
have promised a large degree of API stability. In particular, we will not make
|
| 60 |
+
backward-incompatible changes to released APIs without also shipping a tool or
|
| 61 |
+
process that can upgrade our users' code. We are not yet at the point of easily
|
| 62 |
+
releasing such tools. Therefore, at this time, shipping a library establishes an
|
| 63 |
+
API contract which is borderline unchangeable. (We can add new functionality,
|
| 64 |
+
but we cannot easily change existing behavior.) This constraint forces us to
|
| 65 |
+
very carefully review all APIs that we ship.
|
| 66 |
+
|
| 67 |
+
|
| 68 |
+
## Coding Style
|
| 69 |
+
|
| 70 |
+
To keep the source consistent, readable, diffable and easy to merge, we use a
|
| 71 |
+
fairly rigid coding style, as defined by the
|
| 72 |
+
[google-styleguide](https://github.com/google/styleguide) project. All patches
|
| 73 |
+
will be expected to conform to the style outlined
|
| 74 |
+
[here](https://google.github.io/styleguide/cppguide.html).
|
| 75 |
+
|
| 76 |
+
## Guidelines for Pull Requests
|
| 77 |
+
|
| 78 |
+
* If you are a Googler, it is required that you send us a Piper CL instead of
|
| 79 |
+
using the GitHub pull-request process. The code propagation process will
|
| 80 |
+
deliver the change to GitHub.
|
| 81 |
+
|
| 82 |
+
* Create **small PRs** that are narrowly focused on **addressing a single
|
| 83 |
+
concern**. We often receive PRs that are trying to fix several things at a
|
| 84 |
+
time, but if only one fix is considered acceptable, nothing gets merged and
|
| 85 |
+
both author's & review's time is wasted. Create more PRs to address
|
| 86 |
+
different concerns and everyone will be happy.
|
| 87 |
+
|
| 88 |
+
* For speculative changes, consider opening an [Abseil
|
| 89 |
+
issue](https://github.com/abseil/abseil-cpp/issues) and discussing it first.
|
| 90 |
+
If you are suggesting a behavioral or API change, consider starting with an
|
| 91 |
+
[Abseil proposal template](ABSEIL_ISSUE_TEMPLATE.md).
|
| 92 |
+
|
| 93 |
+
* Provide a good **PR description** as a record of **what** change is being
|
| 94 |
+
made and **why** it was made. Link to a GitHub issue if it exists.
|
| 95 |
+
|
| 96 |
+
* Don't fix code style and formatting unless you are already changing that
|
| 97 |
+
line to address an issue. Formatting of modified lines may be done using
|
| 98 |
+
`git clang-format`. PRs with irrelevant changes won't be merged. If
|
| 99 |
+
you do want to fix formatting or style, do that in a separate PR.
|
| 100 |
+
|
| 101 |
+
* Unless your PR is trivial, you should expect there will be reviewer comments
|
| 102 |
+
that you'll need to address before merging. We expect you to be reasonably
|
| 103 |
+
responsive to those comments, otherwise the PR will be closed after 2-3
|
| 104 |
+
weeks of inactivity.
|
| 105 |
+
|
| 106 |
+
* Maintain **clean commit history** and use **meaningful commit messages**.
|
| 107 |
+
PRs with messy commit history are difficult to review and won't be merged.
|
| 108 |
+
Use `rebase -i upstream/master` to curate your commit history and/or to
|
| 109 |
+
bring in latest changes from master (but avoid rebasing in the middle of a
|
| 110 |
+
code review).
|
| 111 |
+
|
| 112 |
+
* Keep your PR up to date with upstream/master (if there are merge conflicts,
|
| 113 |
+
we can't really merge your change).
|
| 114 |
+
|
| 115 |
+
* **All tests need to be passing** before your change can be merged. We
|
| 116 |
+
recommend you **run tests locally** (see below)
|
| 117 |
+
|
| 118 |
+
* Exceptions to the rules can be made if there's a compelling reason for doing
|
| 119 |
+
so. That is - the rules are here to serve us, not the other way around, and
|
| 120 |
+
the rules need to be serving their intended purpose to be valuable.
|
| 121 |
+
|
| 122 |
+
* All submissions, including submissions by project members, require review.
|
| 123 |
+
|
| 124 |
+
## Running Tests
|
| 125 |
+
|
| 126 |
+
If you have [Bazel](https://bazel.build/) installed, use `bazel test
|
| 127 |
+
--test_tag_filters="-benchmark" ...` to run the unit tests.
|
| 128 |
+
|
| 129 |
+
If you are running the Linux operating system and have
|
| 130 |
+
[Docker](https://www.docker.com/) installed, you can also run the `linux_*.sh`
|
| 131 |
+
scripts under the `ci/`(https://github.com/abseil/abseil-cpp/tree/master/ci)
|
| 132 |
+
directory to test Abseil under a variety of conditions.
|
| 133 |
+
|
| 134 |
+
## Abseil Committers
|
| 135 |
+
|
| 136 |
+
The current members of the Abseil engineering team are the only committers at
|
| 137 |
+
present.
|
| 138 |
+
|
| 139 |
+
## Release Process
|
| 140 |
+
|
| 141 |
+
Abseil lives at head, where latest-and-greatest code can be found.
|
weight/_dep/abseil-cpp/FAQ.md
ADDED
|
@@ -0,0 +1,167 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Abseil FAQ
|
| 2 |
+
|
| 3 |
+
## Is Abseil the right home for my utility library?
|
| 4 |
+
|
| 5 |
+
Most often the answer to the question is "no." As both the [About
|
| 6 |
+
Abseil](https://abseil.io/about/) page and our [contributing
|
| 7 |
+
guidelines](https://github.com/abseil/abseil-cpp/blob/master/CONTRIBUTING.md#contribution-guidelines)
|
| 8 |
+
explain, Abseil contains a variety of core C++ library code that is widely used
|
| 9 |
+
at [Google](https://www.google.com/). As such, Abseil's primary purpose is to be
|
| 10 |
+
used as a dependency by Google's open source C++ projects. While we do hope that
|
| 11 |
+
Abseil is also useful to the C++ community at large, this added constraint also
|
| 12 |
+
means that we are unlikely to accept a contribution of utility code that isn't
|
| 13 |
+
already widely used by Google.
|
| 14 |
+
|
| 15 |
+
## How to I set the C++ dialect used to build Abseil?
|
| 16 |
+
|
| 17 |
+
The short answer is that whatever mechanism you choose, you need to make sure
|
| 18 |
+
that you set this option consistently at the global level for your entire
|
| 19 |
+
project. If, for example, you want to set the C++ dialect to C++17, with
|
| 20 |
+
[Bazel](https://bazel/build/) as the build system and `gcc` or `clang` as the
|
| 21 |
+
compiler, there several ways to do this:
|
| 22 |
+
* Pass `--cxxopt=-std=c++17` on the command line (for example, `bazel build
|
| 23 |
+
--cxxopt=-std=c++17 ...`)
|
| 24 |
+
* Set the environment variable `BAZEL_CXXOPTS` (for example,
|
| 25 |
+
`BAZEL_CXXOPTS=-std=c++17`)
|
| 26 |
+
* Add `build --cxxopt=-std=c++17` to your [`.bazelrc`
|
| 27 |
+
file](https://docs.bazel.build/versions/master/guide.html#bazelrc)
|
| 28 |
+
|
| 29 |
+
If you are using CMake as the build system, you'll need to add a line like
|
| 30 |
+
`set(CMAKE_CXX_STANDARD 17)` to your top level `CMakeLists.txt` file. If you
|
| 31 |
+
are developing a library designed to be used by other clients, you should
|
| 32 |
+
instead leave `CMAKE_CXX_STANDARD` unset and configure the minimum C++ standard
|
| 33 |
+
required by each of your library targets via `target_compile_features`. See the
|
| 34 |
+
[CMake build
|
| 35 |
+
instructions](https://github.com/abseil/abseil-cpp/blob/master/CMake/README.md)
|
| 36 |
+
for more information.
|
| 37 |
+
|
| 38 |
+
For a longer answer to this question and to understand why some other approaches
|
| 39 |
+
don't work, see the answer to ["What is ABI and why don't you recommend using a
|
| 40 |
+
pre-compiled version of
|
| 41 |
+
Abseil?"](#what-is-abi-and-why-dont-you-recommend-using-a-pre-compiled-version-of-abseil)
|
| 42 |
+
|
| 43 |
+
## What is ABI and why don't you recommend using a pre-compiled version of Abseil?
|
| 44 |
+
|
| 45 |
+
For the purposes of this discussion, you can think of
|
| 46 |
+
[ABI](https://en.wikipedia.org/wiki/Application_binary_interface) as the
|
| 47 |
+
compiled representation of the interfaces in code. This is in contrast to
|
| 48 |
+
[API](https://en.wikipedia.org/wiki/Application_programming_interface), which
|
| 49 |
+
you can think of as the interfaces as defined by the code itself. [Abseil has a
|
| 50 |
+
strong promise of API compatibility, but does not make any promise of ABI
|
| 51 |
+
compatibility](https://abseil.io/about/compatibility). Let's take a look at what
|
| 52 |
+
this means in practice.
|
| 53 |
+
|
| 54 |
+
You might be tempted to do something like this in a
|
| 55 |
+
[Bazel](https://bazel.build/) `BUILD` file:
|
| 56 |
+
|
| 57 |
+
```
|
| 58 |
+
# DON'T DO THIS!!!
|
| 59 |
+
cc_library(
|
| 60 |
+
name = "my_library",
|
| 61 |
+
srcs = ["my_library.cc"],
|
| 62 |
+
copts = ["-std=c++17"], # May create a mixed-mode compile!
|
| 63 |
+
deps = ["@com_google_absl//absl/strings"],
|
| 64 |
+
)
|
| 65 |
+
```
|
| 66 |
+
|
| 67 |
+
Applying `-std=c++17` to an individual target in your `BUILD` file is going to
|
| 68 |
+
compile that specific target in C++17 mode, but it isn't going to ensure the
|
| 69 |
+
Abseil library is built in C++17 mode, since the Abseil library itself is a
|
| 70 |
+
different build target. If your code includes an Abseil header, then your
|
| 71 |
+
program may contain conflicting definitions of the same
|
| 72 |
+
class/function/variable/enum, etc. As a rule, all compile options that affect
|
| 73 |
+
the ABI of a program need to be applied to the entire build on a global basis.
|
| 74 |
+
|
| 75 |
+
C++ has something called the [One Definition
|
| 76 |
+
Rule](https://en.wikipedia.org/wiki/One_Definition_Rule) (ODR). C++ doesn't
|
| 77 |
+
allow multiple definitions of the same class/function/variable/enum, etc. ODR
|
| 78 |
+
violations sometimes result in linker errors, but linkers do not always catch
|
| 79 |
+
violations. Uncaught ODR violations can result in strange runtime behaviors or
|
| 80 |
+
crashes that can be hard to debug.
|
| 81 |
+
|
| 82 |
+
If you build the Abseil library and your code using different compile options
|
| 83 |
+
that affect ABI, there is a good chance you will run afoul of the One Definition
|
| 84 |
+
Rule. Examples of GCC compile options that affect ABI include (but aren't
|
| 85 |
+
limited to) language dialect (e.g. `-std=`), optimization level (e.g. `-O2`),
|
| 86 |
+
code generation flags (e.g. `-fexceptions`), and preprocessor defines
|
| 87 |
+
(e.g. `-DNDEBUG`).
|
| 88 |
+
|
| 89 |
+
If you use a pre-compiled version of Abseil, (for example, from your Linux
|
| 90 |
+
distribution package manager or from something like
|
| 91 |
+
[vcpkg](https://github.com/microsoft/vcpkg)) you have to be very careful to
|
| 92 |
+
ensure ABI compatibility across the components of your program. The only way you
|
| 93 |
+
can be sure your program is going to be correct regarding ABI is to ensure
|
| 94 |
+
you've used the exact same compile options as were used to build the
|
| 95 |
+
pre-compiled library. This does not mean that Abseil cannot work as part of a
|
| 96 |
+
Linux distribution since a knowledgeable binary packager will have ensured that
|
| 97 |
+
all packages have been built with consistent compile options. This is one of the
|
| 98 |
+
reasons we warn against - though do not outright reject - using Abseil as a
|
| 99 |
+
pre-compiled library.
|
| 100 |
+
|
| 101 |
+
Another possible way that you might afoul of ABI issues is if you accidentally
|
| 102 |
+
include two versions of Abseil in your program. Multiple versions of Abseil can
|
| 103 |
+
end up within the same binary if your program uses the Abseil library and
|
| 104 |
+
another library also transitively depends on Abseil (resulting in what is
|
| 105 |
+
sometimes called the diamond dependency problem). In cases such as this you must
|
| 106 |
+
structure your build so that all libraries use the same version of Abseil.
|
| 107 |
+
[Abseil's strong promise of API compatibility between
|
| 108 |
+
releases](https://abseil.io/about/compatibility) means the latest "HEAD" release
|
| 109 |
+
of Abseil is almost certainly the right choice if you are doing as we recommend
|
| 110 |
+
and building all of your code from source.
|
| 111 |
+
|
| 112 |
+
For these reasons we recommend you avoid pre-compiled code and build the Abseil
|
| 113 |
+
library yourself in a consistent manner with the rest of your code.
|
| 114 |
+
|
| 115 |
+
## What is "live at head" and how do I do it?
|
| 116 |
+
|
| 117 |
+
From Abseil's point-of-view, "live at head" means that every Abseil source
|
| 118 |
+
release (which happens on an almost daily basis) is either API compatible with
|
| 119 |
+
the previous release, or comes with an automated tool that you can run over code
|
| 120 |
+
to make it compatible. In practice, the need to use an automated tool is
|
| 121 |
+
extremely rare. This means that upgrading from one source release to another
|
| 122 |
+
should be a routine practice that can and should be performed often.
|
| 123 |
+
|
| 124 |
+
We recommend you update to the [latest commit in the `master` branch of
|
| 125 |
+
Abseil](https://github.com/abseil/abseil-cpp/commits/master) as often as
|
| 126 |
+
possible. Not only will you pick up bug fixes more quickly, but if you have good
|
| 127 |
+
automated testing, you will catch and be able to fix any [Hyrum's
|
| 128 |
+
Law](https://www.hyrumslaw.com/) dependency problems on an incremental basis
|
| 129 |
+
instead of being overwhelmed by them and having difficulty isolating them if you
|
| 130 |
+
wait longer between updates.
|
| 131 |
+
|
| 132 |
+
If you are using the [Bazel](https://bazel.build/) build system and its
|
| 133 |
+
[external dependencies](https://docs.bazel.build/versions/master/external.html)
|
| 134 |
+
feature, updating the
|
| 135 |
+
[`http_archive`](https://docs.bazel.build/versions/master/repo/http.html#http_archive)
|
| 136 |
+
rule in your
|
| 137 |
+
[`WORKSPACE`](https://docs.bazel.build/versions/master/be/workspace.html) for
|
| 138 |
+
`com_google_abseil` to point to the [latest commit in the `master` branch of
|
| 139 |
+
Abseil](https://github.com/abseil/abseil-cpp/commits/master) is all you need to
|
| 140 |
+
do. For example, on February 11, 2020, the latest commit to the master branch
|
| 141 |
+
was `98eb410c93ad059f9bba1bf43f5bb916fc92a5ea`. To update to this commit, you
|
| 142 |
+
would add the following snippet to your `WORKSPACE` file:
|
| 143 |
+
|
| 144 |
+
```
|
| 145 |
+
http_archive(
|
| 146 |
+
name = "com_google_absl",
|
| 147 |
+
urls = ["https://github.com/abseil/abseil-cpp/archive/98eb410c93ad059f9bba1bf43f5bb916fc92a5ea.zip"], # 2020-02-11T18:50:53Z
|
| 148 |
+
strip_prefix = "abseil-cpp-98eb410c93ad059f9bba1bf43f5bb916fc92a5ea",
|
| 149 |
+
sha256 = "aabf6c57e3834f8dc3873a927f37eaf69975d4b28117fc7427dfb1c661542a87",
|
| 150 |
+
)
|
| 151 |
+
```
|
| 152 |
+
|
| 153 |
+
To get the `sha256` of this URL, run `curl -sL --output -
|
| 154 |
+
https://github.com/abseil/abseil-cpp/archive/98eb410c93ad059f9bba1bf43f5bb916fc92a5ea.zip
|
| 155 |
+
| sha256sum -`.
|
| 156 |
+
|
| 157 |
+
You can commit the updated `WORKSPACE` file to your source control every time
|
| 158 |
+
you update, and if you have good automated testing, you might even consider
|
| 159 |
+
automating this.
|
| 160 |
+
|
| 161 |
+
One thing we don't recommend is using GitHub's `master.zip` files (for example
|
| 162 |
+
[https://github.com/abseil/abseil-cpp/archive/master.zip](https://github.com/abseil/abseil-cpp/archive/master.zip)),
|
| 163 |
+
which are always the latest commit in the `master` branch, to implement live at
|
| 164 |
+
head. Since these `master.zip` URLs are not versioned, you will lose build
|
| 165 |
+
reproducibility. In addition, some build systems, including Bazel, will simply
|
| 166 |
+
cache this file, which means you won't actually be updating to the latest
|
| 167 |
+
release until your cache is cleared or invalidated.
|
weight/_dep/abseil-cpp/MODULE.bazel
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright 2024 The Abseil Authors.
|
| 2 |
+
#
|
| 3 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
| 4 |
+
# you may not use this file except in compliance with the License.
|
| 5 |
+
# You may obtain a copy of the License at
|
| 6 |
+
#
|
| 7 |
+
# https://www.apache.org/licenses/LICENSE-2.0
|
| 8 |
+
#
|
| 9 |
+
# Unless required by applicable law or agreed to in writing, software
|
| 10 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
| 11 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 12 |
+
# See the License for the specific language governing permissions and
|
| 13 |
+
# limitations under the License.
|
| 14 |
+
|
| 15 |
+
# https://bazel.build/external/overview#bzlmod
|
| 16 |
+
|
| 17 |
+
module(
|
| 18 |
+
name = "abseil-cpp",
|
| 19 |
+
version = "20240116.2",
|
| 20 |
+
compatibility_level = 1,
|
| 21 |
+
)
|
| 22 |
+
|
| 23 |
+
# Only direct dependencies need to be listed below.
|
| 24 |
+
# Please keep the versions in sync with the versions in the WORKSPACE file.
|
| 25 |
+
|
| 26 |
+
bazel_dep(name = "bazel_skylib",
|
| 27 |
+
version = "1.5.0")
|
| 28 |
+
|
| 29 |
+
bazel_dep(name = "google_benchmark",
|
| 30 |
+
version = "1.8.3",
|
| 31 |
+
repo_name = "com_github_google_benchmark",
|
| 32 |
+
dev_dependency = True)
|
| 33 |
+
|
| 34 |
+
bazel_dep(name = "googletest",
|
| 35 |
+
version = "1.14.0.bcr.1",
|
| 36 |
+
repo_name = "com_google_googletest")
|
| 37 |
+
|
| 38 |
+
bazel_dep(name = "platforms",
|
| 39 |
+
version = "0.0.8")
|
weight/_dep/abseil-cpp/PrivacyInfo.xcprivacy
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<?xml version="1.0" encoding="UTF-8"?>
|
| 2 |
+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
| 3 |
+
<plist version="1.0">
|
| 4 |
+
<dict>
|
| 5 |
+
<key>NSPrivacyTracking</key>
|
| 6 |
+
<false/>
|
| 7 |
+
<key>NSPrivacyCollectedDataTypes</key>
|
| 8 |
+
<array/>
|
| 9 |
+
<key>NSPrivacyTrackingDomains</key>
|
| 10 |
+
<array/>
|
| 11 |
+
<key>NSPrivacyAccessedAPITypes</key>
|
| 12 |
+
<array/>
|
| 13 |
+
</dict>
|
| 14 |
+
</plist>
|