00001 #ifndef CCTBX_MAPTBX_GRID_TAGS_H
00002 #define CCTBX_MAPTBX_GRID_TAGS_H
00003
00004 #include <cctbx/maptbx/accessors/c_grid_p1.h>
00005 #include <cctbx/maptbx/accessors/c_grid_padded_p1.h>
00006 #include <cctbx/sgtbx/search_symmetry.h>
00007 #include <cctbx/tagged_value.h>
00008 #include <scitbx/rational.h>
00009 #include <scitbx/array_family/versa.h>
00010 #include <scitbx/array_family/accessors/c_grid.h>
00011 #include <scitbx/array_family/accessors/c_grid_padded.h>
00012 #include <scitbx/array_family/loops.h>
00013 #include <scitbx/math/linear_correlation.h>
00014
00015 namespace cctbx { namespace maptbx {
00016
00017 namespace grid_tags_detail {
00018
00019
00020 struct space_group_symmetry_tag {};
00021 struct seminvariant_tag {};
00022
00023 template <typename DimTupleType>
00024 DimTupleType
00025 factors_for_common_denominator(DimTupleType const& n)
00026 {
00027 typename DimTupleType::value_type lcm = scitbx::array_lcm(n);
00028 DimTupleType result;
00029 for(std::size_t i=0;i<n.size();i++) {
00030 CCTBX_ASSERT(n[i] != 0);
00031 result[i] = lcm / n[i];
00032 }
00033 return result;
00034 }
00035
00036 template <typename DimTupleType,
00037 typename IndexTupleType>
00038 inline tagged_value<IndexTupleType>
00039 multiply(DimTupleType const& n,
00040 DimTupleType const& f,
00041 sgtbx::rt_mx const& s,
00042 IndexTupleType const& fx)
00043 {
00044 IndexTupleType result = s.r() * fx;
00045 for(std::size_t i=0;i<3;i++) {
00046 result[i] = result[i] * s.t().den()
00047 + s.t()[i] * s.r().den() * f[i] * n[i];
00048 if (result[i] % (s.r().den() * s.t().den() * f[i])) {
00049 return tagged_value<IndexTupleType>(result, false);
00050 }
00051 result[i] /= (s.r().den() * s.t().den() * f[i]);
00052 }
00053 return tagged_value<IndexTupleType>(result, true);
00054 }
00055
00056 template <typename DimensionType,
00057 typename GridSsType,
00058 typename IndexTupleType,
00059 typename FactorTupleType>
00060 inline tagged_value<IndexTupleType>
00061 add(DimensionType const& dim,
00062 GridSsType const& grid_ss_continuous,
00063 IndexTupleType const& pivot,
00064 FactorTupleType const& f)
00065 {
00066 IndexTupleType result = pivot;
00067 for(std::size_t i_ss=0;i_ss<grid_ss_continuous.size();i_ss++) {
00068 for(std::size_t i=0;i<3;i++) {
00069 int s = dim[i] * grid_ss_continuous[i_ss].v[i] * f[i_ss];
00070 if (s % grid_ss_continuous[i_ss].m) {
00071 return tagged_value<IndexTupleType>(result, false);
00072 }
00073 result[i] += s / grid_ss_continuous[i_ss].m;
00074 }
00075 }
00076 return tagged_value<IndexTupleType>(result, true);
00077 }
00078
00079 template <typename TagVersaType,
00080 typename IndexTupleType>
00081 std::size_t
00082 mark_orbit(TagVersaType& p1_tags,
00083 sgtbx::space_group const& space_group,
00084 IndexTupleType const& pivot,
00085 space_group_symmetry_tag)
00086 {
00087 std::size_t grid_misses = 0;
00088 if (space_group.order_z() > 1) {
00089 typedef typename TagVersaType::accessor_type dim_tuple_type;
00090 dim_tuple_type f = factors_for_common_denominator(p1_tags.accessor());
00091 IndexTupleType f_pivot;
00092 for(std::size_t i=0;i<3;i++) f_pivot[i] = f[i] * pivot[i];
00093 std::size_t i1d_pivot = p1_tags.accessor()(pivot);
00094 for(std::size_t i_smx=1;i_smx<space_group.order_z();i_smx++) {
00095 sgtbx::rt_mx s = space_group(i_smx);
00096 tagged_value<IndexTupleType>
00097 sym_equiv_point = multiply(p1_tags.accessor(), f, s, f_pivot);
00098 if (sym_equiv_point.tag) {
00099 std::size_t i1d_sep = p1_tags.accessor()(sym_equiv_point.value);
00100 while (p1_tags[i1d_sep] != -1) i1d_sep = p1_tags[i1d_sep];
00101 if (i1d_sep != i1d_pivot) p1_tags[i1d_sep] = i1d_pivot;
00102 }
00103 else {
00104 grid_misses++;
00105 }
00106 }
00107 }
00108 return grid_misses;
00109 }
00110
00111 template <typename TagVersaType,
00112 typename GridSsType,
00113 typename IndexTupleType>
00114 std::size_t
00115 mark_orbit(TagVersaType& p1_tags,
00116 GridSsType const& grid_ss_continuous,
00117 IndexTupleType const& pivot,
00118 seminvariant_tag)
00119 {
00120 std::size_t grid_misses = 0;
00121 std::size_t i1d_pivot = p1_tags.accessor()(pivot);
00122 af::small<int, 3> moduli(grid_ss_continuous.size());
00123 for(std::size_t i=0;i<grid_ss_continuous.size();i++) {
00124 moduli[i] = grid_ss_continuous[i].m;
00125 }
00126 af::nested_loop<af::small<int, 3> > loop(moduli);
00127 for (af::small<int, 3> const& f = loop(); !loop.over(); loop.incr()) {
00128 tagged_value<IndexTupleType>
00129 sym_equiv_point = add(p1_tags.accessor(), grid_ss_continuous, pivot, f);
00130 if (sym_equiv_point.tag) {
00131 std::size_t i1d_sep = p1_tags.accessor()(sym_equiv_point.value);
00132 while (p1_tags[i1d_sep] != -1) i1d_sep = p1_tags[i1d_sep];
00133 if (i1d_sep != i1d_pivot) p1_tags[i1d_sep] = i1d_pivot;
00134 }
00135 else {
00136 grid_misses++;
00137 }
00138 }
00139 return grid_misses;
00140 }
00141
00142 template <typename TagVersaType,
00143 typename SymmetryType,
00144 typename SymmetryTypeTag>
00145 std::size_t
00146 mark_orbits(TagVersaType& p1_tags,
00147 SymmetryType const& symmetry,
00148 SymmetryTypeTag)
00149 {
00150 std::size_t grid_misses = 0;
00151 af::nested_loop<af::int3> loop(p1_tags.accessor());
00152 for (af::int3 const& pivot = loop(); !loop.over(); loop.incr()) {
00153 if (p1_tags(pivot) == -1) {
00154 grid_misses += mark_orbit(p1_tags, symmetry, pivot, SymmetryTypeTag());
00155 }
00156 }
00157 return grid_misses;
00158 }
00159
00160 template <typename TagArrayType>
00161 std::size_t
00162 optimize_tags(TagArrayType tags)
00163 {
00164 std::size_t n_independent = 0;
00165 for(std::size_t i=0;i<tags.size();i++) {
00166 if (tags[i] < 0) {
00167 n_independent++;
00168 }
00169 else {
00170 std::size_t j = tags[i];
00171 while (tags[j] >= 0) j = tags[j];
00172 tags[i] = j;
00173 }
00174 }
00175 return n_independent;
00176 }
00177
00178 template <typename FloatType, typename TagType>
00179 scitbx::math::linear_correlation<>
00180 dependent_correlation(
00181 std::size_t n_dependent,
00182 af::const_ref<FloatType, af::c_grid_padded<3> > const& data,
00183 af::const_ref<TagType, c_grid_p1<3> > const& tags,
00184 double epsilon)
00185 {
00186 CCTBX_ASSERT(tags.accessor().all_eq(data.accessor().focus()));
00187 typedef af::c_grid_padded<3>::index_type index_type;
00188 af::nested_loop<index_type> loop(data.accessor().focus());
00189 af::c_grid<3> tags_accessor(tags.accessor());
00190 std::vector<FloatType> x; x.reserve(n_dependent);
00191 std::vector<FloatType> y; y.reserve(n_dependent);
00192 std::size_t i = 0;
00193 for (index_type const& pt = loop(); !loop.over(); loop.incr(), i++) {
00194 if (tags[i] < 0) continue;
00195 x.push_back(data(pt));
00196 y.push_back(data(tags_accessor.index_nd(tags[i])));
00197 }
00198 CCTBX_ASSERT(x.size() == n_dependent);
00199 return scitbx::math::linear_correlation<>(
00200 af::make_const_ref(x),
00201 af::make_const_ref(y),
00202 epsilon);
00203 }
00204
00205 }
00206
00207 template <typename TagType = long>
00208 class grid_tags
00209 {
00210 public:
00211 typedef af::versa<TagType, c_grid_p1<3> > tag_array_type;
00212 typedef typename tag_array_type::accessor_type tag_accessor_type;
00213 typedef typename tag_accessor_type::index_type grid_point_type;
00214
00215 grid_tags() {}
00216
00217 grid_tags(af::int3 const& dim)
00218 :
00219 is_valid_(false),
00220 tag_array_(dim)
00221 {}
00222
00223 bool
00224 is_valid() const { return is_valid_; }
00225
00226 af::versa<TagType, c_grid_p1<3> >
00227 tag_array() const { return tag_array_; }
00228
00229 void
00230 build(sgtbx::space_group_type const& sg_type,
00231 sgtbx::search_symmetry_flags const& symmetry_flags);
00232
00233 sgtbx::space_group_type const&
00234 space_group_type() const { return sg_type_; }
00235
00236 sgtbx::search_symmetry_flags const&
00237 symmetry_flags() const { return symmetry_flags_; }
00238
00239 af::small<sgtbx::ss_vec_mod, 3> const&
00240 grid_ss_continuous() const { return grid_ss_continuous_; }
00241
00242 std::size_t
00243 n_grid_misses() const { return n_grid_misses_; }
00244
00245 std::size_t
00246 n_independent() const { return n_independent_; }
00247
00248 std::size_t
00249 n_dependent() const { return tag_array_.size() - n_independent_; }
00250
00251 template <typename FloatType>
00252 scitbx::math::linear_correlation<>
00253 dependent_correlation(
00254 af::const_ref<FloatType, af::c_grid_padded<3> > const& data,
00255 double epsilon=1.e-15) const
00256 {
00257 CCTBX_ASSERT(is_valid_);
00258 CCTBX_ASSERT(tag_array_.accessor().all_eq(data.accessor().focus()));
00259 return grid_tags_detail::dependent_correlation(
00260 n_dependent(),
00261 data,
00262 tag_array_.const_ref(),
00263 epsilon);
00264 }
00265
00266 template <typename FloatType>
00267 bool
00268 verify(
00269 af::const_ref<FloatType, af::c_grid_padded<3> > const& data,
00270 double min_correlation=0.99) const
00271 {
00272 if (n_dependent() == 0) return true;
00273 return dependent_correlation(data).coefficient() >= min_correlation;
00274 }
00275
00276
00277 template <typename FloatType>
00278 void
00279 sum_sym_equiv_points(
00280 af::ref<FloatType, c_grid_padded_p1<3> > const& data) const
00281 {
00282 CCTBX_ASSERT(is_valid_);
00283 CCTBX_ASSERT(tag_array_.accessor().all_eq(data.accessor().focus()));
00284 using namespace grid_tags_detail;
00285 {
00286
00287 tag_accessor_type
00288 f = grid_tags_detail::factors_for_common_denominator(
00289 tag_array_.accessor());
00290 sgtbx::space_group const& space_group = sg_type_.group();
00291 af::nested_loop<grid_point_type> loop(tag_array_.accessor());
00292 for (grid_point_type const& pt = loop(); !loop.over(); loop.incr()) {
00293 if (tag_array_(pt) >= 0) continue;
00294 std::size_t i_pt = data.accessor()(pt);
00295 FloatType sum = data[i_pt];
00296 if (space_group.order_z() > 1) {
00297 grid_point_type f_pt;
00298 for(std::size_t i=0;i<3;i++) f_pt[i] = f[i] * pt[i];
00299 for(std::size_t i_smx=1;i_smx<space_group.order_z();i_smx++) {
00300 sgtbx::rt_mx m = space_group(i_smx);
00301 tagged_value<grid_point_type>
00302 sym_equiv_point = grid_tags_detail::multiply(
00303 tag_array_.accessor(), f, m, f_pt);
00304 CCTBX_ASSERT(sym_equiv_point.tag);
00305 sum += data(sym_equiv_point.value);
00306 }
00307 }
00308 data[i_pt] = sum;
00309 }
00310 }
00311 {
00312
00313 af::nested_loop<grid_point_type> loop(tag_array_.accessor());
00314 for (grid_point_type const& pt = loop(); !loop.over(); loop.incr()) {
00315 if (tag_array_(pt) < 0) continue;
00316 grid_point_type
00317 asym_pt = tag_array_.accessor().index_nd(tag_array_(pt));
00318 data(pt) = data(asym_pt);
00319 }
00320 }
00321 }
00322
00323 template <typename DataType>
00324 std::size_t
00325 apply_symmetry_to_mask(
00326 af::ref<DataType, af::c_grid<3> > const& data) const
00327 {
00328 CCTBX_ASSERT(tag_array_.accessor().all_eq(data.accessor()));
00329 std::size_t n_overlap = 0;
00330 const TagType* tags=tag_array_.begin();
00331 for(std::size_t i=0;i<data.size();i++) {
00332 if (tags[i] < 0) continue;
00333 if (data[i] == 0) {
00334 TagType j = tags[i];
00335 if (data[j] == 0) {
00336 n_overlap++;
00337 }
00338 else {
00339 data[j] = 0;
00340 }
00341 }
00342 }
00343 for(std::size_t i=0;i<data.size();i++) {
00344 if (tags[i] < 0) continue;
00345 data[i] = data[tags[i]];
00346 }
00347 return n_overlap;
00348 }
00349
00350 protected:
00351 bool is_valid_;
00352 tag_array_type tag_array_;
00353 sgtbx::space_group_type sg_type_;
00354 sgtbx::search_symmetry_flags symmetry_flags_;
00355 af::small<sgtbx::ss_vec_mod, 3> grid_ss_continuous_;
00356 std::size_t n_grid_misses_;
00357 std::size_t n_independent_;
00358 };
00359
00360 template <typename TagType>
00361 void
00362 grid_tags<TagType>
00363 ::build(sgtbx::space_group_type const& sg_type,
00364 sgtbx::search_symmetry_flags const& symmetry_flags)
00365 {
00366 using namespace grid_tags_detail;
00367 if ( is_valid_
00368 && sg_type_.group() == sg_type.group()
00369 && symmetry_flags_ == symmetry_flags) {
00370 return;
00371 }
00372 sg_type_ = sg_type;
00373 symmetry_flags_ = symmetry_flags;
00374 n_grid_misses_ = 0;
00375 tag_array_.fill(-1);
00376 sgtbx::structure_seminvariants ss;
00377 sgtbx::space_group sym;
00378 if (symmetry_flags.use_seminvariants()) {
00379 ss = sgtbx::structure_seminvariants(sg_type.group());
00380 sym = sgtbx::search_symmetry(symmetry_flags_, sg_type_, ss).subgroup();
00381 }
00382 else {
00383 sym = sgtbx::search_symmetry(symmetry_flags_, sg_type_).subgroup();
00384 }
00385 if (mark_orbits(tag_array_, sym, space_group_symmetry_tag()) > 0) {
00386 throw error("Grid is not compatible with symmetry.");
00387 }
00388 if (symmetry_flags.use_seminvariants()) {
00389 grid_ss_continuous_ = ss.select(false).grid_adapted_moduli(
00390 tag_array_.accessor());
00391 n_grid_misses_ = mark_orbits(
00392 tag_array_, grid_ss_continuous_, seminvariant_tag());
00393 }
00394 n_independent_ = grid_tags_detail::optimize_tags(tag_array_.as_1d().ref());
00395 is_valid_ = true;
00396 }
00397
00398 }}
00399
00400 #endif // CCTBX_MAPTBX_GRID_TAGS_H