Commit a8c07e87 authored by Tauriello Gerardo's avatar Tauriello Gerardo

Merge branch 'release-2.1.0'

parents abda7388 cce41036
......@@ -5,6 +5,19 @@
Changelog
================================================================================
Release 2.1.0
--------------------------------------------------------------------------------
* This is expected to be the last release supporting Python 2.
* This project now requires a C++11 compatible compiler.
* Introduced VINA scoring function in the sidechain module. A scoring function
specific RotamerConstructor is provided that comes with extensive heuristics
to parametrize arbitrary compounds.
* Motif finding algorithm to identify objects in 3D space, e.g. binding sites.
The algorithm is based on principles of geometric hashing.
* Several minor bug fixes, improvements, and speed-ups
Release 2.0.0
--------------------------------------------------------------------------------
......
......@@ -16,7 +16,7 @@ include(PROMOD3)
# versioning info
set(PROMOD3_VERSION_MAJOR 2)
set(PROMOD3_VERSION_MINOR 0)
set(PROMOD3_VERSION_MINOR 1)
set(PROMOD3_VERSION_PATCH 0)
set(PROMOD3_VERSION_STRING ${PROMOD3_VERSION_MAJOR}.${PROMOD3_VERSION_MINOR})
set(PROMOD3_VERSION_STRING ${PROMOD3_VERSION_STRING}.${PROMOD3_VERSION_PATCH})
......@@ -93,7 +93,7 @@ if(NOT DISABLE_DOCUMENTATION)
# this URL should always point to the latest version of OST
set(OST_DOC_URL "https://www.openstructure.org/docs")
endif()
find_package(OPENSTRUCTURE 1.10.0 REQUIRED
find_package(OPENSTRUCTURE 1.11.0 REQUIRED
COMPONENTS io mol seq seq_alg mol_alg conop img mol_mm)
if(CMAKE_COMPILER_IS_GNUCXX)
......
......@@ -1000,6 +1000,11 @@ macro(setup_compiler_flags)
# -fno-strict-aliasing
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-strict-aliasing" )
endif()
#message(STATUS "GCC VERSION " ${_GCC_VERSION})
if (_GCC_VERSION LESS "60")
# for older compilers we need to enable C++11
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
endif()
endif()
if(_ENABLE_SSE)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -msse4" )
......
......@@ -2,8 +2,8 @@ FROM ubuntu:18.04
# ARGUMENTS
###########
ARG OPENSTRUCTURE_VERSION="1.10.0"
ARG PROMOD_VERSION="2.0.0"
ARG OPENSTRUCTURE_VERSION="1.11.0"
ARG PROMOD_VERSION="2.1.0"
ARG SRC_FOLDER="/usr/local/src"
ARG CPUS_FOR_MAKE=4
ARG COMPLIB_DIR="/usr/local/share/ost_complib"
......
......@@ -18,35 +18,56 @@
#define PROMOD3_EIGEN_TYPES_HH
#include <ost/base.hh>
#include <ost/geom/vecmat3_op.hh>
#include <Eigen/Dense>
#include <Eigen/StdVector>
namespace promod3 { namespace core {
// some quadratic matrices
typedef Eigen::Matrix<Real,3,3> EMat3;
typedef Eigen::Matrix<Real,4,4> EMat4;
typedef Eigen::Matrix<double,8,8> EMat8;
typedef Eigen::Matrix<double,16,16> EMat16;
// some quadratic matrices
typedef Eigen::Matrix<Real,3,3> EMat3;
typedef Eigen::Matrix<Real,4,4> EMat4;
typedef Eigen::Matrix<double,8,8> EMat8;
typedef Eigen::Matrix<double,16,16> EMat16;
typedef Eigen::Matrix<Real,3,1> EVec3;
typedef Eigen::Matrix<Real,4,1> EVec4;
typedef Eigen::Matrix<Real,3,1> EVec3;
typedef Eigen::Matrix<Real,4,1> EVec4;
typedef Eigen::Matrix<Real,Eigen::Dynamic,1> EVecX;
typedef Eigen::Matrix<Real,1,3> ERVec3;
typedef Eigen::Matrix<Real,1,4> ERVec4;
typedef Eigen::Matrix<Real,1,3> ERVec3;
typedef Eigen::Matrix<Real,1,4> ERVec4;
typedef Eigen::Matrix<Real,1,Eigen::Dynamic> ERVecX;
// some special matrices used at various locations
typedef Eigen::Matrix<Real,Eigen::Dynamic,3> EMatX3;
typedef Eigen::Matrix<Real,3,Eigen::Dynamic> EMat3X;
typedef Eigen::Matrix<Real, Eigen::Dynamic, Eigen::Dynamic> EMatXX;
typedef Eigen::Matrix<double,16,3> EMat16_3;
// some special matrices used at various locations
typedef Eigen::Matrix<Real,Eigen::Dynamic,3> EMatX3;
typedef Eigen::Matrix<Real, Eigen::Dynamic, Eigen::Dynamic> EMatXX;
typedef Eigen::Matrix<double,16,3> EMat16_3;
// If you want to use stl containers of fixed sized Eigentype (e.g. EMatX3)
// you must use a custom allocator provided by Eigen to ensure proper memory
// alignment (more on that in the Eigen documentation)
typedef std::vector<EMatX3,Eigen::aligned_allocator<EMatX3> > EMatX3List;
typedef std::vector<EMatX3,Eigen::aligned_allocator<EMat3X> > EMat3XList;
// If you want to use stl containers of fixed sized Eigentype (e.g. EMatX3)
// you must use a custom allocator provided by Eigen to ensure proper memory
// alignment (more on that in the Eigen documentation)
typedef std::vector<EMatX3,Eigen::aligned_allocator<EMatX3> > EMatX3List;
// Fill row of given Eigen Matrix with 3 entries of Vec3
// NOTE: this is 60% faster than "tst.row(row) = core::ERVec3(&v[0]);"
template <typename EMat>
inline void EMatFillRow(EMat& mat, uint row, const geom::Vec3& v) {
mat(row, 0) = v[0];
mat(row, 1) = v[1];
mat(row, 2) = v[2];
}
// Same for col
template <typename EMat>
inline void EMatFillCol(EMat& mat, uint col, const geom::Vec3& v) {
mat(0, col) = v[0];
mat(1, col) = v[1];
mat(2, col) = v[2];
}
}} // ns
......
......@@ -48,11 +48,12 @@ void TheobaldRMSD(const promod3::core::EMatX3& pos_one,
"superpose!");
}
promod3::core::EMat3 M = pos_one.transpose() * pos_two;
// using floats when calculating M might lead to numerical instabilities
// later on, so let's cast to double precision
Eigen::Matrix<double,3,3> M =
pos_one.cast<double>().transpose() * pos_two.cast<double>();
// for the calculations itself we enforce double precision
// for example the newton optimization doesn't converge nicely if
// Real is a float
// using floats for the squared norm is fine
double GA = pos_one.squaredNorm();
double GB = pos_two.squaredNorm();
......
......@@ -84,16 +84,6 @@ void RigidBlocks(EMatX3& pos_one, EMatX3& pos_two,
std::vector<std::vector<uint> >& indices,
std::vector<geom::Mat4>& transformations);
// Fill row of given Eigen Matrix with 3 entries of Vec3
// NOTE: this is 60% faster than "tst.row(row) = core::ERVec3(&v[0]);"
template <typename EMat>
inline void EMatFillRow(EMat& mat, uint row, const geom::Vec3& v) {
mat(row, 0) = v[0];
mat(row, 1) = v[1];
mat(row, 2) = v[2];
}
}} //ns
#endif
......@@ -45,6 +45,9 @@ void Fill(const std::vector<int>& indices,
std::vector<int>& l,
std::vector<int>& r){
l.reserve(indices.size());
r.reserve(indices.size());
for(std::vector<int>::const_iterator i = indices.begin(),
e = indices.end(); i != e; ++i){
if(values[*i] < boundary) l.push_back(*i);
......@@ -135,8 +138,22 @@ void TetrahedralPolytopeTree::ResetTree(const std::vector<Real>& x,
}
num_leaf_nodes_ = x.size();
nodes_.resize(num_leaf_nodes_);
connectivity_.resize(num_leaf_nodes_);
if(num_leaf_nodes_ == 0) {
// empty tree... assign some default values, even though it doesn't matter
// too much as any call for the Overlapp functions is guarded by a check for
// num_leaf_nodes_ == 0
root_node_ = 0;
nodes_.resize(0);
connectivity_.resize(0);
return;
}
// we construct a full binary tree (each node has either 0 or 2 children)
// the number of nodes can therefore be determined from the number of leafs
int num_nodes = 2*num_leaf_nodes_-1;
nodes_.resize(num_nodes);
connectivity_.resize(num_nodes);
std::vector<int> indices(num_leaf_nodes_);
for(int i = 0; i < num_leaf_nodes_; ++i){
......@@ -145,10 +162,7 @@ void TetrahedralPolytopeTree::ResetTree(const std::vector<Real>& x,
indices[i] = i;
}
if(num_leaf_nodes_ == 0) {
return; // tree is empty... we don't even have to call Generate
}
idx_helper_ = num_leaf_nodes_;
root_node_ = this->Generate(x, y, z, indices);
}
......@@ -158,16 +172,32 @@ int TetrahedralPolytopeTree::Generate(const std::vector<Real>& x,
const std::vector<Real>& z,
const std::vector<int>& indices){
// it it's a leaf, it's already contructed in the ResetTree
if(indices.size() == 1){
// the leaf nodes are already added in the ResetTree function
return indices[0];
}
int return_idx = nodes_.size();
// determine index of that node from idx_helper and construct its Polytope
int return_idx = idx_helper_++;
this->AssignPolytope(x, y, x, indices, return_idx);
// separate the members and construct the children
std::vector<int> l,r;
Separate(x, y, z, indices, l, r);
connectivity_[return_idx].first = this->Generate(x, y, z, l);
connectivity_[return_idx].second = this->Generate(x, y, z, r);
return return_idx;
}
void TetrahedralPolytopeTree::AssignPolytope(const std::vector<Real>& x,
const std::vector<Real>& y,
const std::vector<Real>& z,
const std::vector<int>& indices,
int node_idx) {
// find extent of tetrahedral polytope containing all polytopes
// defined in indices
#if PM3_ENABLE_SSE && OST_DOUBLE_PRECISION == 0
__m128 min_bounds = _mm_set1_ps(std::numeric_limits<Real>::max());
__m128 max_bounds = _mm_set1_ps(-std::numeric_limits<Real>::max());
......@@ -209,20 +239,7 @@ int TetrahedralPolytopeTree::Generate(const std::vector<Real>& x,
}
#endif
nodes_.push_back(TetrahedralPolytope(min_bounds, max_bounds));
connectivity_.push_back(std::make_pair(-1, -1));
std::vector<int> l,r;
l.reserve(indices.size());
r.reserve(indices.size());
Separate(x, y, z, indices, l, r);
connectivity_[return_idx].first = this->Generate(x, y, z, l);
connectivity_[return_idx].second = this->Generate(x, y, z, r);
return return_idx;
nodes_[node_idx] = TetrahedralPolytope(min_bounds, max_bounds);
}
}} // ns
......@@ -148,6 +148,12 @@ private:
const std::vector<Real>& z,
const std::vector<int>& indices);
void AssignPolytope(const std::vector<Real>& x,
const std::vector<Real>& y,
const std::vector<Real>& z,
const std::vector<int>& indices,
int node_idx);
inline void TraverseTree(const TetrahedralPolytopeTree& other_tree,
int this_idx, int other_idx,
std::vector<std::pair<int,int> >& result) const{
......@@ -180,6 +186,9 @@ private:
int num_leaf_nodes_;
std::vector<TetrahedralPolytope> nodes_;
std::vector<std::pair<int, int> > connectivity_;
// helper variable to keep track of recursive Generate calls
int idx_helper_;
};
}} // ns
......
......@@ -18,7 +18,6 @@
#include <promod3/core/portable_binary_serializer.hh>
#define BOOST_TEST_DYN_LINK
#include <boost/test/unit_test.hpp>
#include <boost/test/auto_unit_test.hpp>
#include <ost/base.hh>
#include <cstdio>
......
......@@ -18,7 +18,6 @@
#include <promod3/core/graph_minimizer.hh>
#define BOOST_TEST_DYN_LINK
#include <boost/test/unit_test.hpp>
#include <boost/test/auto_unit_test.hpp>
#include <ost/base.hh>
#include <vector>
......
......@@ -17,7 +17,6 @@
#include <promod3/core/portable_binary_serializer.hh>
#define BOOST_TEST_DYN_LINK
#include <boost/test/unit_test.hpp>
#include <boost/test/auto_unit_test.hpp>
#include <ost/base.hh>
#include <cstdio>
......
......@@ -16,6 +16,4 @@
#define BOOST_TEST_DYN_LINK
#define BOOST_TEST_MODULE promod3_core
#define BOOST_AUTO_TEST_MAIN
#include <boost/test/unit_test.hpp>
#include <boost/test/auto_unit_test.hpp>
......@@ -24,21 +24,21 @@ Dependencies
--------------------------------------------------------------------------------
|project| is build on top of |ost_l|_ (|ost_s|), requiring at least version
|ost_version|. |ost_s| must be configured and compiled with ``ENABLE_MM=1`` to
use |openmm|_. To create the build system, |cmake|_ is required. The same
versions of |python|_ and |boost|_ are needed as used in |ost_s|. For |eigen3|_
we need at least version 3.3.0. To build the documentation, |sphinx|_ is
required.
|ost_version|. A C++11 compatible compiler is required. |ost_s| must be
configured and compiled with ``ENABLE_MM=1`` to use |openmm|_. To create the
build system, |cmake|_ is required. The same versions of |python|_ and |boost|_
are needed as used in |ost_s|. For |eigen3|_ we need at least version 3.3.0. To
build the documentation, |sphinx|_ is required.
The currently preferred versions are:
* |ost_s|_ |ost_version|
* |openmm|_ 7.1.1
* |cmake|_ 2.8.12
* |python|_ 2.7.5
* |python|_ 2.7.11
* |boost|_ 1.53.0
* |eigen3|_ 3.3.0
* |sphinx|_ 1.3.1
* |eigen3|_ 3.3.1
* |sphinx|_ 1.4.1
--------------------------------------------------------------------------------
Using |cmake|
......
......@@ -58,7 +58,7 @@ master_doc = 'index'
# General information about the project.
project = u'ProMod3'
copyright = u'2013-2019, SIB - Swiss Institute of Bioinformatics and Biozentrum - University of Basel'# pylint: disable=redefined-builtin
copyright = u'2013-2020, SIB - Swiss Institute of Bioinformatics and Biozentrum - University of Basel'# pylint: disable=redefined-builtin
# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
......@@ -286,7 +286,7 @@ rst_epilog = """
.. |cmake| replace:: CMake
.. |ost_l| replace:: OpenStructure
.. |ost_s| replace:: OST
.. |ost_version| replace:: 1.10.0
.. |ost_version| replace:: 1.11.0
.. |python| replace:: Python
.. |sphinx| replace:: Sphinx
.. _sphinx: http://sphinx-doc.org/
......
......@@ -36,6 +36,8 @@ Fire the local Registry and push the promod image to it:
sudo docker tag promod localhost:5000/promod
sudo docker push localhost:5000/promod
If port 5000 is already taken on your machine, use a different ``<PORT>`` by
using the flag ``-p <PORT>:5000`` and ``localhost:<PORT>`` in these commands.
Make sure, that on top of your Singularity recipe you have something like:
.. code-block:: bash
......@@ -71,7 +73,24 @@ To get help on how to run it:
.. code-block:: bash
singularity run --app Notebook <IMAGE> --help
singularity help --app Notebook <IMAGE>
Within the notebook you can test OST, ProMod3 and nglview as follows:
.. code-block:: python
from ost import io
from promod3 import loop
import nglview
# generate backbone with dihedrals of a helix and store it
sequence = "HELLYEAH"
bb_list = loop.BackboneList(sequence)
io.SavePDB(bb_list.ToEntity(), "test.pdb")
# display stored file
view = nglview.show_file("test.pdb")
view
The Compound Library
......
......@@ -24,21 +24,21 @@ Dependencies
--------------------------------------------------------------------------------
|project| is build on top of |ost_l|_ (|ost_s|), requiring at least version
|ost_version|. |ost_s| must be configured and compiled with ``ENABLE_MM=1`` to
use |openmm|_. To create the build system, |cmake|_ is required. The same
versions of |python|_ and |boost|_ are needed as used in |ost_s|. For |eigen3|_
we need at least version 3.3.0. To build the documentation, |sphinx|_ is
required.
|ost_version|. A C++11 compatible compiler is required. |ost_s| must be
configured and compiled with ``ENABLE_MM=1`` to use |openmm|_. To create the
build system, |cmake|_ is required. The same versions of |python|_ and |boost|_
are needed as used in |ost_s|. For |eigen3|_ we need at least version 3.3.0. To
build the documentation, |sphinx|_ is required.
The currently preferred versions are:
* |ost_s|_ |ost_version|
* |openmm|_ 7.1.1
* |cmake|_ 2.8.12
* |python|_ 2.7.5
* |python|_ 2.7.11
* |boost|_ 1.53.0
* |eigen3|_ 3.3.0
* |sphinx|_ 1.3.1
* |eigen3|_ 3.3.1
* |sphinx|_ 1.4.1
--------------------------------------------------------------------------------
Using |cmake|
......
......@@ -5,6 +5,19 @@
Changelog
================================================================================
Release 2.1.0
--------------------------------------------------------------------------------
* This is expected to be the last release supporting Python 2.
* This project now requires a C++11 compatible compiler.
* Introduced VINA scoring function in the sidechain module. A scoring function
specific RotamerConstructor is provided that comes with extensive heuristics
to parametrize arbitrary compounds.
* Motif finding algorithm to identify objects in 3D space, e.g. binding sites.
The algorithm is based on principles of geometric hashing.
* Several minor bug fixes, improvements, and speed-ups
Release 2.0.0
--------------------------------------------------------------------------------
......
......@@ -36,6 +36,8 @@ Fire the local Registry and push the promod image to it:
sudo docker tag promod localhost:5000/promod
sudo docker push localhost:5000/promod
If port 5000 is already taken on your machine, use a different ``<PORT>`` by
using the flag ``-p <PORT>:5000`` and ``localhost:<PORT>`` in these commands.
Make sure, that on top of your Singularity recipe you have something like:
.. code-block:: bash
......@@ -71,7 +73,24 @@ To get help on how to run it:
.. code-block:: bash
singularity run --app Notebook <IMAGE> --help
singularity help --app Notebook <IMAGE>
Within the notebook you can test OST, ProMod3 and nglview as follows:
.. code-block:: python
from ost import io
from promod3 import loop
import nglview
# generate backbone with dihedrals of a helix and store it
sequence = "HELLYEAH"
bb_list = loop.BackboneList(sequence)
io.SavePDB(bb_list.ToEntity(), "test.pdb")
# display stored file
view = nglview.show_file("test.pdb")
view
The Compound Library
......
......@@ -79,6 +79,22 @@ The BackboneList class
code which is not one of the 20 default amino acids or if
*sequence* and *dihedral_angles* are inconsistent in size.
.. method:: BackboneList(residues)
Creates a BackboneList with positions and sequence extracted from
*residues*.
:param residues: List of :class:`ost.mol.ResidueHandle` objects from
which the backbone positions and one letter codes
are extracted.
:type residues: :class:`list`
:raises: :exc:`~exceptions.RuntimeError` if a residue in *residues*
contains a one letter code which is not one of the 20 default
amino acids or when there is a residue not providing all
required positions.
.. method:: BackboneList(sequence, residues)
Creates a BackboneList from given *sequence* and positions extracted from
......
This diff is collapsed.
......@@ -164,3 +164,165 @@ example pipeline.
.. autofunction:: GenerateDeNovoTrajectories
Motif Finder
--------------------------------------------------------------------------------
Distinct spatial arrangements of atoms or functional groups are key for protein
function. For their detection, ProMod3 implements the MotifFinder algorithm
which is based on geometric hashing as described by Nussinov and Wolfson
[nussinov1991]_. The algorithm consists of a learning stage, a detection stage
and a refinement stage.
Learning Stage: A motif (query) is represented by a set of coordinates. Triplets
(p1, p2, p3) of coordinates are selected that define triangles. For each
triangle one can define an orthogonal vector basis
(in our case v1 = norm(p2-p1), v3 = norm(cross(v1,p3-p1),
v2 = norm(cross(v1,v3)))). For each coordinate not in [p1,p2,p3], we add the
identity of the query/triangle as value to a hash map.
The corresponding key consists of discretized values describing the edge lengths
of the triangle, as well as the coordinate transformed into the triangle
specific orthogonal vector basis. That's 6 numbers in total.
Detection Stage: The goal is to identify one or several subsets of target
coordinates that resemble an input query.
We first setup an accumulator containing a counter for each triangle observed
in the input query. We then iterate over each possible triangle with vertices
p1, p2 and p3 in the target coordinates. At the beginning of each iteration,
all counters in the accumulator are set to zero. Again, we build a vector basis
given that triangle and transform all coordinates not in [p1,p2,p3] into that
vector space. For each transformed coordinate we obtain a key for the query hash
map. If there is one or several values at that location in the hash map,
we increment the corresponding locations in the accumulator.
Once all coordinates are processed, we search for high counts in the
accumulator. Given *N* query coordinates, we keep a solution for further
refinement if count/(*N*-3) >= *hash_tresh*. This is repeated until all
triangles in the target are processed. One key problem with this approach is
the discretization of floating point numbers that give raise to the hash map
keys. Two extremely close values might end up in different bins just because
they are close to the bin boundaries. For each of the 6 relevant numbers
we estimate the actual bin as well as the closest neighbouring bin. Processing
all possible combinations results in 64 hash map lookups instead of only one.
Refinement Stage: Every potential solution identified in the detection stage is
further refined based on the *distance_thresh* and *refine_thresh* parameters.
A potential solution found in the detection stage is a pair of triangles, one
in the query and one in the target, for which we find many matching coordinates
in their respective vector space. We start with a coordinate mapping based on
the triangle vertices from the query and the target (3 pairs).
This coordinate mapping is iteratively updated by estimating the minimum RMSD
superposition of the mapped query coordinates onto the target, apply that
superposition on the query, find the closest target coordinate for each
coordinate in the query and redo the mapping by including all pairs with
minimum distance < *distance_thresh*. Iteration stops if nothing changes
anymore. The solution is returned to the user if the final fraction of mapped
query coordinates is larger or equal *refine_thresh*.
The larger the mapping, the more accurate the superposition. As we start with
only the three triangle vertices, *distance_thresh* is doubled for the initial
iteration.
.. literalinclude:: ../../../tests/doc/scripts/modelling_motif_finder.py
.. class:: MotifQuery(positions, identifier, min_triangle_edge_length, \
max_triangle_edge_length, bin_size)
MotifQuery(positions, identifier, min_triangle_edge_length, \
max_triangle_edge_length, bin_size, flags)
MotifQuery(query_list)
A single query or a container of queries.
The constructor performs the learning stage of a single query or combines
several queries, so they can be searched at once.
:param positions: Coordinates of the query
:param identifier: Descriptor of the query
:param min_triangle_edge_length: To avoid the full O(n^3) hell, triangles
with any edge length below *min_triangle_edge_length*
are skipped
:param max_triangle_edge_length: Same as *min_triangle_edge_length* but
upper bound
:param bin_size: Bin size in A, relevant to generate hash map keys
:param flags: Flag in range [0,63] for every coordinate in *positions*.
They're also added to the hash map keys (default: 0).
This means that additionally to having a matching
relative position, the coordinates must also have a
matching flag in the detection/refinement stage.
If not provided (in the query and in the search),
only coordinates matter.
:param query_list: E pluribus unum
:type positions: :class:`ost.geom.Vec3List`
:type identifier: :class:`str`
:type min_triangle_edge_length: :class:`float`
:type max_triangle_edge_length: :class:`float`
:type bin_size: :class:`float`
:type flags: :class:`list` of :class:`int`
:type query_list: :class:`list` of :class:`MotifQuery`
.. method:: Save(filename)
Saves the query down to disk
:param filename: filename
:type filename: :class:`str`
.. staticmethod:: Load(filename)
Load query from disk
:param filename: filename
:type filename: :class:`str`
.. method:: GetPositions(query_idx)
Returns coordinates of specified query
:param query_idx: Query from which you want the positions
:type query_idx: :class:`int`
.. method:: GetIdentifiers()
Returns a list of all query identifiers.
.. method:: GetN()
Returns the number of queries
.. class:: MotifMatch
Object that holds information about a match found in :meth:`FindMotifs`
.. attribute:: query_idx
Index of matching query
.. attribute:: mat
Transformation matrix to superpose matching query onto target
.. attribute:: alignment
List of tuples which define matching pairs of query/target coordinates