From 9fda1829f5a6dec22e88e1bc2af18f549c3a6d91 Mon Sep 17 00:00:00 2001 From: Isuru Fernando Date: Sun, 31 May 2020 17:22:23 -0500 Subject: [PATCH 01/11] Add TranslationClassesBuilder and make RotationClassesBuilder depend on that --- boxtree/rotation_classes.py | 252 +---------------- boxtree/translation_classes.py | 488 +++++++++++++++++++++++++++++++++ test/test_traversal.py | 25 +- 3 files changed, 517 insertions(+), 248 deletions(-) create mode 100644 boxtree/translation_classes.py diff --git a/boxtree/rotation_classes.py b/boxtree/rotation_classes.py index 58f1c0a..54e8fa2 100644 --- a/boxtree/rotation_classes.py +++ b/boxtree/rotation_classes.py @@ -23,14 +23,10 @@ THE SOFTWARE. """ import numpy as np -from pytools import Record, memoize_method import pyopencl as cl import pyopencl.array # noqa -import pyopencl.cltypes # noqa -from pyopencl.elementwise import ElementwiseTemplate -from mako.template import Template -from boxtree.tools import DeviceDataRecord, InlineBinarySearch -from boxtree.traversal import TRAVERSAL_PREAMBLE_MAKO_DEFS +from boxtree.tools import DeviceDataRecord +from boxtree.translation_classes import TranslationClassesBuilder import logging logger = logging.getLogger(__name__) @@ -40,127 +36,6 @@ from pytools import log_process # {{{ rotation classes builder -# Note that these kernels compute translation classes first, and -# these get converted to rotation classes in a second step, from Python. - -TRANSLATION_CLASS_FINDER_PREAMBLE_TEMPLATE = Template(r"""//CL:mako// - #define LEVEL_TO_RAD(level) \ - (root_extent * 1 / (coord_t) (1 << (level + 1))) - - // Return an integer vector indicating the a translation direction - // as a multiple of the box diameter. - inline int_coord_vec_t get_translation_vector( - coord_t root_extent, - int level, - coord_vec_t source_center, - coord_vec_t target_center) - { - int_coord_vec_t result = (int_coord_vec_t) 0; - coord_t diam = 2 * LEVEL_TO_RAD(level); - %for i in range(dimensions): - result.s${i} = rint((target_center.s${i} - source_center.s${i}) / diam); - %endfor - return result; - } - - // Compute the translation class for the given translation vector. The - // translation class maps a translation vector (a_1, a_2, ..., a_d) into - // a dense range of integers [0, ..., (4*n+3)^d - 1], where - // d is the dimension and n is well_sep_is_n_away. - // - // This relies on the fact that the entries of the vector will - // always be in the range [-2n-1,...,2n+1]. - // - // The mapping from vector to class is: - // - // \~~ d k-1 - // cls(a ,a ,...,a ) = > (2n+1+a ) (4n+3) - // 1 2 d /__ k=1 k - // - // Returns -1 on error. - inline int get_translation_class(int_coord_vec_t vec, int well_sep_is_n_away) - { - int dim_bound = 2 * well_sep_is_n_away + 1; - %for i in range(dimensions): - if (!(-dim_bound <= vec.s${i} && vec.s${i} <= dim_bound)) - { - return -1; - } - %endfor - - int result = 0; - int base = 4 * well_sep_is_n_away + 3; - int mult = 1; - %for i in range(dimensions): - result += (2 * well_sep_is_n_away + 1 + vec.s${i}) * mult; - mult *= base; - %endfor - return result; - } - """ + str(InlineBinarySearch("box_id_t")), - strict_undefined=True) - - -TRANSLATION_CLASS_FINDER_TEMPLATE = ElementwiseTemplate( - arguments=r"""//CL:mako// - /* input: */ - box_id_t *from_sep_siblings_lists, - box_id_t *from_sep_siblings_starts, - box_id_t *target_or_target_parent_boxes, - int ntarget_or_target_parent_boxes, - coord_t *box_centers, - int aligned_nboxes, - coord_t root_extent, - box_level_t *box_levels, - int well_sep_is_n_away, - - /* output: */ - int *translation_classes, - int *translation_class_is_used, - int *error_flag, - """, - - operation=TRAVERSAL_PREAMBLE_MAKO_DEFS + r"""//CL:mako// - // Find the target box for this source box. - box_id_t source_box_id = from_sep_siblings_lists[i]; - - size_t itarget_box = bsearch( - from_sep_siblings_starts, 1 + ntarget_or_target_parent_boxes, i); - - box_id_t target_box_id = target_or_target_parent_boxes[itarget_box]; - - // Ensure levels are the same. - if (box_levels[source_box_id] != box_levels[target_box_id]) - { - atomic_or(error_flag, 1); - PYOPENCL_ELWISE_CONTINUE; - } - - // Compute the translation vector and translation class. - ${load_center("source_center", "source_box_id")} - ${load_center("target_center", "target_box_id")} - - int_coord_vec_t vec = get_translation_vector( - root_extent, box_levels[source_box_id], source_center, target_center); - - int translation_class = get_translation_class(vec, well_sep_is_n_away); - - // Ensure valid translation class. - if (translation_class == -1) - { - atomic_or(error_flag, 1); - PYOPENCL_ELWISE_CONTINUE; - } - - translation_classes[i] = translation_class; - atomic_or(&translation_class_is_used[translation_class], 1); - """) - - -class _KernelInfo(Record): - pass - - class RotationClassesInfo(DeviceDataRecord): r"""Interaction lists to help with matrix precomputations for rotation-based translations ("point and shoot"). @@ -191,58 +66,7 @@ class RotationClassesInfo(DeviceDataRecord): return len(self.from_sep_siblings_rotation_class_to_angle) -class RotationClassesBuilder(object): - """Build rotation classes for List 2 translations. - """ - - def __init__(self, context): - self.context = context - - @memoize_method - def get_kernel_info(self, dimensions, well_sep_is_n_away, - box_id_dtype, box_level_dtype, coord_dtype): - coord_vec_dtype = cl.cltypes.vec_types[coord_dtype, dimensions] - int_coord_vec_dtype = cl.cltypes.vec_types[np.dtype(np.int32), dimensions] - - # Make sure translation classes can fit inside a 32 bit integer. - if (not - ( - self.ntranslation_classes(well_sep_is_n_away, dimensions) - <= 1 + np.iinfo(np.int32).max)): - raise ValueError("would overflow") - - preamble = TRANSLATION_CLASS_FINDER_PREAMBLE_TEMPLATE.render( - dimensions=dimensions) - - translation_class_finder = ( - TRANSLATION_CLASS_FINDER_TEMPLATE.build( - self.context, - type_aliases=( - ("int_coord_vec_t", int_coord_vec_dtype), - ("coord_vec_t", coord_vec_dtype), - ("coord_t", coord_dtype), - ("box_id_t", box_id_dtype), - ("box_level_t", box_level_dtype), - ), - var_values=( - ("dimensions", dimensions), - ), - more_preamble=preamble)) - - return _KernelInfo(translation_class_finder=translation_class_finder) - - @staticmethod - def vec_gcd(vec): - """Return the GCD of a list of integers.""" - def gcd(a, b): - while b: - a, b = b, a % b - return a - - result = abs(vec[0]) - for elem in vec[1:]: - result = gcd(result, abs(elem)) - return result +class RotationClassesBuilder(TranslationClassesBuilder): def compute_rotation_classes(self, well_sep_is_n_away, dimensions, used_translation_classes): @@ -292,83 +116,27 @@ class RotationClassesBuilder(object): return translation_class_to_rot_class, angles - @staticmethod - def ntranslation_classes(well_sep_is_n_away, dimensions): - return (4 * well_sep_is_n_away + 3) ** dimensions - - @staticmethod - def translation_class_to_vector(well_sep_is_n_away, dimensions, cls): - # This computes the vector for the translation class, using the inverse - # of the formula found in get_translation_class() defined in - # TRANSLATION_CLASS_FINDER_PREAMBLE_TEMPLATE. - result = np.zeros(dimensions, dtype=np.int32) - shift = 2 * well_sep_is_n_away + 1 - base = 4 * well_sep_is_n_away + 3 - for i in range(dimensions): - result[i] = cls % base - shift - cls //= base - return result - @log_process(logger, "build m2l rotation classes") def __call__(self, queue, trav, tree, wait_for=None): """Returns a pair *info*, *evt* where info is a :class:`RotationClassesInfo`. """ + evt, translation_class_is_used, translation_classes_lists = \ + self.compute_translation_classes(queue, trav, tree, wait_for, False) - # {{{ compute translation classes for list 2 + d = tree.dimensions + n = trav.well_sep_is_n_away - well_sep_is_n_away = trav.well_sep_is_n_away - dimensions = tree.dimensions - coord_dtype = tree.coord_dtype - - knl_info = self.get_kernel_info( - dimensions, well_sep_is_n_away, tree.box_id_dtype, - tree.box_level_dtype, coord_dtype) - - ntranslation_classes = ( - self.ntranslation_classes(well_sep_is_n_away, dimensions)) - - translation_classes_lists = cl.array.empty( - queue, len(trav.from_sep_siblings_lists), dtype=np.int32) - - translation_class_is_used = cl.array.zeros( - queue, ntranslation_classes, dtype=np.int32) - - error_flag = cl.array.zeros(queue, 1, dtype=np.int32) - - evt = knl_info.translation_class_finder( - trav.from_sep_siblings_lists, - trav.from_sep_siblings_starts, - trav.target_or_target_parent_boxes, - trav.ntarget_or_target_parent_boxes, - tree.box_centers, - tree.aligned_nboxes, - tree.root_extent, - tree.box_levels, - well_sep_is_n_away, - translation_classes_lists, - translation_class_is_used, - error_flag, - queue=queue, wait_for=wait_for) - - if (error_flag.get()): - raise ValueError("could not compute translation classes") - - # }}} - - # {{{ convert translation classes to rotation classes + # convert translation classes to rotation classes used_translation_classes = ( np.flatnonzero(translation_class_is_used.get())) translation_class_to_rotation_class, rotation_angles = ( - self.compute_rotation_classes( - well_sep_is_n_away, dimensions, used_translation_classes)) + self.compute_rotation_classes(n, d, used_translation_classes)) # There should be no more than 2^(d-1) * (2n+1)^d distinct rotation # classes, since that is an upper bound on the number of distinct # positions for list 2 boxes. - d = dimensions - n = well_sep_is_n_away assert len(rotation_angles) <= 2**(d-1) * (2*n+1)**d rotation_classes_lists = ( @@ -378,8 +146,6 @@ class RotationClassesBuilder(object): rotation_angles = cl.array.to_device(queue, np.array(rotation_angles)) - # }}} - return RotationClassesInfo( from_sep_siblings_rotation_classes=rotation_classes_lists, from_sep_siblings_rotation_class_to_angle=rotation_angles, diff --git a/boxtree/translation_classes.py b/boxtree/translation_classes.py new file mode 100644 index 0000000..eebed1e --- /dev/null +++ b/boxtree/translation_classes.py @@ -0,0 +1,488 @@ +from __future__ import division + +__copyright__ = "Copyright (C) 2019 Matt Wala" + +__license__ = """ +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +""" + +import numpy as np +from pytools import Record, memoize_method +import pyopencl as cl +import pyopencl.array # noqa +import pyopencl.cltypes # noqa +from pyopencl.elementwise import ElementwiseTemplate +from mako.template import Template +from boxtree.tools import DeviceDataRecord, InlineBinarySearch +from boxtree.traversal import TRAVERSAL_PREAMBLE_MAKO_DEFS + +import logging +logger = logging.getLogger(__name__) + +from pytools import log_process + + +# {{{ translation classes builder + +TRANSLATION_CLASS_FINDER_PREAMBLE_TEMPLATE = Template(r"""//CL:mako// + #define LEVEL_TO_RAD(level) \ + (root_extent * 1 / (coord_t) (1 << (level + 1))) + + // Return an integer vector indicating the a translation direction + // as a multiple of the box diameter. + inline int_coord_vec_t get_translation_vector( + coord_t root_extent, + int level, + coord_vec_t source_center, + coord_vec_t target_center) + { + int_coord_vec_t result = (int_coord_vec_t) 0; + coord_t diam = 2 * LEVEL_TO_RAD(level); + %for i in range(dimensions): + result.s${i} = rint((target_center.s${i} - source_center.s${i}) / diam); + %endfor + return result; + } + + // Compute the translation class for the given translation vector. The + // translation class maps a translation vector (a_1, a_2, ..., a_d) into + // a dense range of integers [0, ..., (4*n+3)^d - 1], where + // d is the dimension and n is well_sep_is_n_away. + // + // This relies on the fact that the entries of the vector will + // always be in the range [-2n-1,...,2n+1]. + // + // The mapping from vector to class is: + // + // \~~ d k-1 + // cls(a ,a ,...,a ) = > (2n+1+a ) (4n+3) + // 1 2 d /__ k=1 k + // + // Returns -1 on error. + inline int get_translation_class(int_coord_vec_t vec, int well_sep_is_n_away) + { + int dim_bound = 2 * well_sep_is_n_away + 1; + %for i in range(dimensions): + if (!(-dim_bound <= vec.s${i} && vec.s${i} <= dim_bound)) + { + return -1; + } + %endfor + + int result = 0; + int base = 4 * well_sep_is_n_away + 3; + int mult = 1; + %for i in range(dimensions): + result += (2 * well_sep_is_n_away + 1 + vec.s${i}) * mult; + mult *= base; + %endfor + return result; + } + """ + str(InlineBinarySearch("box_id_t")), + strict_undefined=True) + + +TRANSLATION_CLASS_FINDER_TEMPLATE = ElementwiseTemplate( + arguments=r"""//CL:mako// + /* input: */ + box_id_t *from_sep_siblings_lists, + box_id_t *from_sep_siblings_starts, + box_id_t *target_or_target_parent_boxes, + int ntarget_or_target_parent_boxes, + coord_t *box_centers, + int aligned_nboxes, + coord_t root_extent, + box_level_t *box_levels, + int well_sep_is_n_away, + + /* output: */ + int *translation_classes, + int *translation_class_is_used, + int *error_flag, + """, + + operation=TRAVERSAL_PREAMBLE_MAKO_DEFS + r"""//CL:mako// + // Find the target box for this source box. + box_id_t source_box_id = from_sep_siblings_lists[i]; + + size_t itarget_box = bsearch( + from_sep_siblings_starts, 1 + ntarget_or_target_parent_boxes, i); + + box_id_t target_box_id = target_or_target_parent_boxes[itarget_box]; + + // Ensure levels are the same. + if (box_levels[source_box_id] != box_levels[target_box_id]) + { + atomic_or(error_flag, 1); + PYOPENCL_ELWISE_CONTINUE; + } + + // Compute the translation vector and translation class. + ${load_center("source_center", "source_box_id")} + ${load_center("target_center", "target_box_id")} + + int_coord_vec_t vec = get_translation_vector( + root_extent, box_levels[source_box_id], source_center, target_center); + + int translation_class = get_translation_class(vec, well_sep_is_n_away); + + // Ensure valid translation class. + if (translation_class == -1) + { + atomic_or(error_flag, 1); + PYOPENCL_ELWISE_CONTINUE; + } + + % if translation_class_per_level: + translation_class += box_levels[source_box_id] * ${ntranslation_classes}; + % endif + + translation_classes[i] = translation_class; + atomic_or(&translation_class_is_used[translation_class], 1); + """) + + +class _KernelInfo(Record): + pass + + +class TranslationClassesInfo(DeviceDataRecord): + r"""Interaction lists to help with for translations that benefit from + precomputing distance related values + + .. attribute:: nfrom_sep_siblings_translation_classes + + The number of distinct rotation classes. + + .. attribute:: from_sep_siblings_translation_classes + + ``int32 [*]`` + + A list, corresponding to *from_sep_siblings_lists* of *trav*, of + the translation classes of each box pair. + + .. attribute:: from_sep_siblings_translation_class_to_distance_vector + + ``coord_t [nfrom_sep_siblings_translation_classes]`` + + Maps translation classes in *from_sep_siblings_translation_classes* to + the distance between the centers. + """ + + @property + def nfrom_sep_siblings_translation_classes(self): + return len(self.from_sep_siblings_translation_class_to_distance_vector) + + +class TranslationClassesBuilder(object): + """Build translation classes for List 2 translations. + """ + + def __init__(self, context, translation_class_per_level=True): + """translation_class_per_level determines whether the class depends on + the level or not. + """ + self.context = context + + @memoize_method + def get_kernel_info(self, dimensions, well_sep_is_n_away, + box_id_dtype, box_level_dtype, coord_dtype, translation_class_per_level): + coord_vec_dtype = cl.cltypes.vec_types[coord_dtype, dimensions] + int_coord_vec_dtype = cl.cltypes.vec_types[np.dtype(np.int32), dimensions] + + num_translation_classes = \ + self.ntranslation_classes(well_sep_is_n_away, dimensions) + + # Make sure translation classes can fit inside a 32 bit integer. + if not num_translation_classes <= 1 + np.iinfo(np.int32).max: + raise ValueError("would overflow") + + preamble = TRANSLATION_CLASS_FINDER_PREAMBLE_TEMPLATE.render( + dimensions=dimensions) + + translation_class_finder = ( + TRANSLATION_CLASS_FINDER_TEMPLATE.build( + self.context, + type_aliases=( + ("int_coord_vec_t", int_coord_vec_dtype), + ("coord_vec_t", coord_vec_dtype), + ("coord_t", coord_dtype), + ("box_id_t", box_id_dtype), + ("box_level_t", box_level_dtype), + ), + var_values=( + ("dimensions", dimensions), + ("ntranslation_classes", num_translation_classes), + ("translation_class_per_level", translation_class_per_level), + ), + more_preamble=preamble)) + + return _KernelInfo(translation_class_finder=translation_class_finder) + + @staticmethod + def vec_gcd(vec): + """Return the GCD of a list of integers.""" + def gcd(a, b): + while b: + a, b = b, a % b + return a + + result = abs(vec[0]) + for elem in vec[1:]: + result = gcd(result, abs(elem)) + return result + + @staticmethod + def ntranslation_classes(well_sep_is_n_away, dimensions): + return (4 * well_sep_is_n_away + 3) ** dimensions + + @staticmethod + def translation_class_to_vector(well_sep_is_n_away, dimensions, cls): + # This computes the vector for the translation class, using the inverse + # of the formula found in get_translation_class() defined in + # TRANSLATION_CLASS_FINDER_PREAMBLE_TEMPLATE. + result = np.zeros(dimensions, dtype=np.int32) + shift = 2 * well_sep_is_n_away + 1 + base = 4 * well_sep_is_n_away + 3 + for i in range(dimensions): + result[i] = cls % base - shift + cls //= base + return result + + def compute_translation_classes(self, queue, trav, tree, wait_for, + is_translation_per_level): + """ + Returns a tuple *evt*, *translation_class_is_used* and + *translation_classes_lists*. + """ + + # {{{ compute translation classes for list 2 + + well_sep_is_n_away = trav.well_sep_is_n_away + dimensions = tree.dimensions + coord_dtype = tree.coord_dtype + + knl_info = self.get_kernel_info( + dimensions, well_sep_is_n_away, tree.box_id_dtype, + tree.box_level_dtype, coord_dtype, is_translation_per_level) + + ntranslation_classes = ( + self.ntranslation_classes(well_sep_is_n_away, dimensions)) + + if is_translation_per_level: + ntranslation_classes = ntranslation_classes * tree.nlevels + + translation_classes_lists = cl.array.empty( + queue, len(trav.from_sep_siblings_lists), dtype=np.int32) + + translation_class_is_used = cl.array.zeros( + queue, ntranslation_classes, dtype=np.int32) + + error_flag = cl.array.zeros(queue, 1, dtype=np.int32) + + evt = knl_info.translation_class_finder( + trav.from_sep_siblings_lists, + trav.from_sep_siblings_starts, + trav.target_or_target_parent_boxes, + trav.ntarget_or_target_parent_boxes, + tree.box_centers, + tree.aligned_nboxes, + tree.root_extent, + tree.box_levels, + well_sep_is_n_away, + translation_classes_lists, + translation_class_is_used, + error_flag, + queue=queue, wait_for=wait_for) + + if (error_flag.get()): + raise ValueError("could not compute translation classes") + + return (evt, translation_class_is_used, translation_classes_lists) + + # }}} + + @log_process(logger, "build m2l translation classes") + def __call__(self, queue, trav, tree, wait_for=None, + is_translation_per_level=True): + """Returns a pair *info*, *evt* where info is a :class:`TranslationClassesInfo`. + """ + evt, translation_class_is_used, translation_classes_lists = \ + self.compute_translation_classes(queue, trav, tree, wait_for, + is_translation_per_level) + + well_sep_is_n_away = trav.well_sep_is_n_away + dimensions = tree.dimensions + + used_translation_classes_map = np.empty(len(translation_class_is_used), + dtype=np.int32) + used_translation_classes_map.fill(-1) + + distances = np.empty((dimensions, len(translation_class_is_used)), + dtype=tree.coord_dtype) + num_translation_classes = self.ntranslation_classes(well_sep_is_n_away, + dimensions) + + count = 0 + for i, used in enumerate(translation_class_is_used.get()): + if not used: + continue + used_translation_classes_map[i] = count + cls_without_level = i % num_translation_classes + level = i // num_translation_classes + unit_vector = self.translation_class_to_vector(well_sep_is_n_away, + dimensions, cls_without_level) + distances[:, count] = unit_vector * tree.root_extent / (1 << level) + count = count + 1 + + translation_classes_lists = ( + cl.array.take( + cl.array.to_device(queue, used_translation_classes_map), + translation_classes_lists)) + + distances = cl.array.to_device(queue, distances) + + return TranslationClassesInfo( + from_sep_siblings_translation_classes=translation_classes_lists, + from_sep_siblings_translation_class_to_distance_vector=distances, + ).with_queue(None), evt + +# }}} + +# {{{ rotation classes builder + + +class RotationClassesInfo(DeviceDataRecord): + r"""Interaction lists to help with matrix precomputations for rotation-based + translations ("point and shoot"). + + .. attribute:: nfrom_sep_siblings_rotation_classes + + The number of distinct rotation classes. + + .. attribute:: from_sep_siblings_rotation_classes + + ``int32 [*]`` + + A list, corresponding to *from_sep_siblings_lists* of *trav*, of + the rotation class of each box pair. + + .. attribute:: from_sep_siblings_rotation_class_to_angle + + ``coord_t [nfrom_sep_siblings_rotation_classes]`` + + Maps rotation classes in *from_sep_siblings_rotation_classes* to + rotation angles. This represents the angle between box translation + pairs and the *z*-axis. + + """ + + @property + def nfrom_sep_siblings_rotation_classes(self): + return len(self.from_sep_siblings_rotation_class_to_angle) + + +class RotationClassesBuilder(TranslationClassesBuilder): + + def compute_rotation_classes(self, + well_sep_is_n_away, dimensions, used_translation_classes): + """Convert translation classes to a list of rotation classes and angles.""" + angle_to_rot_class = {} + angles = [] + + ntranslation_classes = ( + self.ntranslation_classes(well_sep_is_n_away, dimensions)) + + translation_class_to_rot_class = ( + np.empty(ntranslation_classes, dtype=np.int32)) + + translation_class_to_rot_class[:] = -1 + + for cls in used_translation_classes: + vec = self.translation_class_to_vector( + well_sep_is_n_away, dimensions, cls) + + # Normalize the translation vector (by dividing by its GCD). + # + # We need this before computing the cosine of the rotation angle, + # because generally in in floating point arithmetic, if k is a + # positive scalar and v is a vector, we can't assume + # + # kv[-1] / sqrt(|kv|^2) == v[-1] / sqrt(|v|^2). + # + # Normalizing ensures vectors that are positive integer multiples of + # each other get classified into the same equivalence class of + # rotations. + vec //= self.vec_gcd(vec) + + # Compute the rotation angle for the vector. + norm = np.linalg.norm(vec) + assert norm != 0 + angle = np.arccos(vec[-1] / norm) + + # Find the rotation class. + if angle in angle_to_rot_class: + rot_class = angle_to_rot_class[angle] + else: + rot_class = len(angles) + angle_to_rot_class[angle] = rot_class + angles.append(angle) + + translation_class_to_rot_class[cls] = rot_class + + return translation_class_to_rot_class, angles + + @log_process(logger, "build m2l rotation classes") + def __call__(self, queue, trav, tree, wait_for=None): + """Returns a pair *info*, *evt* where info is a :class:`RotationClassesInfo`. + """ + evt, translation_class_is_used, translation_classes_lists = \ + self.compute_translation_classes(queue, trav, tree, wait_for, False) + + d = tree.dimensions + n = trav.well_sep_is_n_away + + # convert translation classes to rotation classes + + used_translation_classes = ( + np.flatnonzero(translation_class_is_used.get())) + + translation_class_to_rotation_class, rotation_angles = ( + self.compute_rotation_classes(n, d, used_translation_classes)) + + # There should be no more than 2^(d-1) * (2n+1)^d distinct rotation + # classes, since that is an upper bound on the number of distinct + # positions for list 2 boxes. + assert len(rotation_angles) <= 2**(d-1) * (2*n+1)**d + + rotation_classes_lists = ( + cl.array.take( + cl.array.to_device(queue, translation_class_to_rotation_class), + translation_classes_lists)) + + rotation_angles = cl.array.to_device(queue, np.array(rotation_angles)) + + return RotationClassesInfo( + from_sep_siblings_rotation_classes=rotation_classes_lists, + from_sep_siblings_rotation_class_to_angle=rotation_angles, + ).with_queue(None), evt + +# }}} + +# vim: filetype=pyopencl:fdm=marker diff --git a/test/test_traversal.py b/test/test_traversal.py index cb8ef80..64b21ed 100644 --- a/test/test_traversal.py +++ b/test/test_traversal.py @@ -358,10 +358,10 @@ def test_plot_traversal(ctx_factory, well_sep_is_n_away=1, plot=False): # }}} -# {{{ test_from_sep_siblings_rotation_classes +# {{{ test_from_sep_siblings_translation_classes @pytest.mark.parametrize("well_sep_is_n_away", (1, 2)) -def test_from_sep_siblings_rotation_classes(ctx_factory, well_sep_is_n_away): +def test_from_sep_siblings_translation_classes(ctx_factory, well_sep_is_n_away): ctx = ctx_factory() queue = cl.CommandQueue(ctx) @@ -391,6 +391,7 @@ def test_from_sep_siblings_rotation_classes(ctx_factory, well_sep_is_n_away): from boxtree.traversal import FMMTraversalBuilder from boxtree.rotation_classes import RotationClassesBuilder + from boxtree.translation_classes import TranslationClassesBuilder tg = FMMTraversalBuilder(ctx, well_sep_is_n_away=well_sep_is_n_away) trav, _ = tg(queue, tree) @@ -398,9 +399,16 @@ def test_from_sep_siblings_rotation_classes(ctx_factory, well_sep_is_n_away): rb = RotationClassesBuilder(ctx) result, _ = rb(queue, trav, tree) + tb = TranslationClassesBuilder(ctx) + result_tb, _ = tb(queue, trav, tree) + rot_classes = result.from_sep_siblings_rotation_classes.get(queue) rot_angles = result.from_sep_siblings_rotation_class_to_angle.get(queue) + translation_classes = result_tb.from_sep_siblings_translation_classes.get(queue) + distance_vectors = \ + result_tb.from_sep_siblings_translation_class_to_distance_vector.get(queue) + tree = tree.get(queue=queue) trav = trav.get(queue=queue) @@ -418,10 +426,17 @@ def test_from_sep_siblings_rotation_classes(ctx_factory, well_sep_is_n_away): seps = trav.from_sep_siblings_lists[start:end] level_rot_classes = rot_classes[start:end] - translation_vecs = centers[tgt_ibox] - centers[seps] + level_translation_classes = translation_classes[start:end] + actual_translation_vecs = distance_vectors[:, level_translation_classes].T + expected_translation_vecs = centers[tgt_ibox] - centers[seps] + + if len(expected_translation_vecs) > 0: + assert np.allclose(actual_translation_vecs, expected_translation_vecs, + atol=1e-13, rtol=1e-13) + theta = np.arctan2( - la.norm(translation_vecs[:, :dims - 1], axis=1), - translation_vecs[:, dims - 1]) + la.norm(expected_translation_vecs[:, :dims - 1], axis=1), + expected_translation_vecs[:, dims - 1]) level_rot_angles = rot_angles[level_rot_classes] assert np.allclose(theta, level_rot_angles, atol=1e-13, rtol=1e-13) -- GitLab From db5b85f573c51623d3c8cc33836d17f610bb6d93 Mon Sep 17 00:00:00 2001 From: Isuru Fernando Date: Tue, 9 Jun 2020 09:13:17 -0500 Subject: [PATCH 02/11] add level starts for translation_classes --- boxtree/translation_classes.py | 34 ++++++++++++++++++++++++++++------ 1 file changed, 28 insertions(+), 6 deletions(-) diff --git a/boxtree/translation_classes.py b/boxtree/translation_classes.py index eebed1e..4c4ad2c 100644 --- a/boxtree/translation_classes.py +++ b/boxtree/translation_classes.py @@ -168,7 +168,7 @@ class TranslationClassesInfo(DeviceDataRecord): .. attribute:: nfrom_sep_siblings_translation_classes - The number of distinct rotation classes. + The number of distinct translation classes. .. attribute:: from_sep_siblings_translation_classes @@ -179,10 +179,17 @@ class TranslationClassesInfo(DeviceDataRecord): .. attribute:: from_sep_siblings_translation_class_to_distance_vector - ``coord_t [nfrom_sep_siblings_translation_classes]`` + ``coord_vec_t [nfrom_sep_siblings_translation_classes]`` + + Maps translation classes in *from_sep_siblings_translation_classes* + to the distance between the centers + + .. attribute:: from_sep_siblings_translation_classes_level_starts - Maps translation classes in *from_sep_siblings_translation_classes* to - the distance between the centers. + ``coord_t [nlevels + 1]`` + + A list with an entry for each level giving the starting translation + class id for that level. """ @property @@ -339,28 +346,43 @@ class TranslationClassesBuilder(object): num_translation_classes = self.ntranslation_classes(well_sep_is_n_away, dimensions) + nlevels = tree.nlevels count = 0 + prev_level = -1 + from_sep_siblings_translation_classes_level_starts = \ + np.empty(nlevels+1, dtype=np.int32) for i, used in enumerate(translation_class_is_used.get()): + cls_without_level = i % num_translation_classes + level = i // num_translation_classes + if (prev_level != level): + from_sep_siblings_translation_classes_level_starts[level] = count + prev_level = level + if not used: continue + used_translation_classes_map[i] = count - cls_without_level = i % num_translation_classes - level = i // num_translation_classes unit_vector = self.translation_class_to_vector(well_sep_is_n_away, dimensions, cls_without_level) distances[:, count] = unit_vector * tree.root_extent / (1 << level) count = count + 1 + from_sep_siblings_translation_classes_level_starts[nlevels] = count + translation_classes_lists = ( cl.array.take( cl.array.to_device(queue, used_translation_classes_map), translation_classes_lists)) distances = cl.array.to_device(queue, distances) + from_sep_siblings_translation_classes_level_starts = cl.array.to_device( + queue, from_sep_siblings_translation_classes_level_starts) return TranslationClassesInfo( from_sep_siblings_translation_classes=translation_classes_lists, from_sep_siblings_translation_class_to_distance_vector=distances, + from_sep_siblings_translation_classes_level_starts=( + from_sep_siblings_translation_classes_level_starts), ).with_queue(None), evt # }}} -- GitLab From b46e018140b9fdf06c9a2cf47fa9e927b4045c00 Mon Sep 17 00:00:00 2001 From: Isuru Fernando Date: Mon, 29 Jun 2020 18:30:04 -0500 Subject: [PATCH 03/11] Remove duplicated code --- boxtree/translation_classes.py | 120 --------------------------------- 1 file changed, 120 deletions(-) diff --git a/boxtree/translation_classes.py b/boxtree/translation_classes.py index 4c4ad2c..d5687fe 100644 --- a/boxtree/translation_classes.py +++ b/boxtree/translation_classes.py @@ -387,124 +387,4 @@ class TranslationClassesBuilder(object): # }}} -# {{{ rotation classes builder - - -class RotationClassesInfo(DeviceDataRecord): - r"""Interaction lists to help with matrix precomputations for rotation-based - translations ("point and shoot"). - - .. attribute:: nfrom_sep_siblings_rotation_classes - - The number of distinct rotation classes. - - .. attribute:: from_sep_siblings_rotation_classes - - ``int32 [*]`` - - A list, corresponding to *from_sep_siblings_lists* of *trav*, of - the rotation class of each box pair. - - .. attribute:: from_sep_siblings_rotation_class_to_angle - - ``coord_t [nfrom_sep_siblings_rotation_classes]`` - - Maps rotation classes in *from_sep_siblings_rotation_classes* to - rotation angles. This represents the angle between box translation - pairs and the *z*-axis. - - """ - - @property - def nfrom_sep_siblings_rotation_classes(self): - return len(self.from_sep_siblings_rotation_class_to_angle) - - -class RotationClassesBuilder(TranslationClassesBuilder): - - def compute_rotation_classes(self, - well_sep_is_n_away, dimensions, used_translation_classes): - """Convert translation classes to a list of rotation classes and angles.""" - angle_to_rot_class = {} - angles = [] - - ntranslation_classes = ( - self.ntranslation_classes(well_sep_is_n_away, dimensions)) - - translation_class_to_rot_class = ( - np.empty(ntranslation_classes, dtype=np.int32)) - - translation_class_to_rot_class[:] = -1 - - for cls in used_translation_classes: - vec = self.translation_class_to_vector( - well_sep_is_n_away, dimensions, cls) - - # Normalize the translation vector (by dividing by its GCD). - # - # We need this before computing the cosine of the rotation angle, - # because generally in in floating point arithmetic, if k is a - # positive scalar and v is a vector, we can't assume - # - # kv[-1] / sqrt(|kv|^2) == v[-1] / sqrt(|v|^2). - # - # Normalizing ensures vectors that are positive integer multiples of - # each other get classified into the same equivalence class of - # rotations. - vec //= self.vec_gcd(vec) - - # Compute the rotation angle for the vector. - norm = np.linalg.norm(vec) - assert norm != 0 - angle = np.arccos(vec[-1] / norm) - - # Find the rotation class. - if angle in angle_to_rot_class: - rot_class = angle_to_rot_class[angle] - else: - rot_class = len(angles) - angle_to_rot_class[angle] = rot_class - angles.append(angle) - - translation_class_to_rot_class[cls] = rot_class - - return translation_class_to_rot_class, angles - - @log_process(logger, "build m2l rotation classes") - def __call__(self, queue, trav, tree, wait_for=None): - """Returns a pair *info*, *evt* where info is a :class:`RotationClassesInfo`. - """ - evt, translation_class_is_used, translation_classes_lists = \ - self.compute_translation_classes(queue, trav, tree, wait_for, False) - - d = tree.dimensions - n = trav.well_sep_is_n_away - - # convert translation classes to rotation classes - - used_translation_classes = ( - np.flatnonzero(translation_class_is_used.get())) - - translation_class_to_rotation_class, rotation_angles = ( - self.compute_rotation_classes(n, d, used_translation_classes)) - - # There should be no more than 2^(d-1) * (2n+1)^d distinct rotation - # classes, since that is an upper bound on the number of distinct - # positions for list 2 boxes. - assert len(rotation_angles) <= 2**(d-1) * (2*n+1)**d - - rotation_classes_lists = ( - cl.array.take( - cl.array.to_device(queue, translation_class_to_rotation_class), - translation_classes_lists)) - - rotation_angles = cl.array.to_device(queue, np.array(rotation_angles)) - - return RotationClassesInfo( - from_sep_siblings_rotation_classes=rotation_classes_lists, - from_sep_siblings_rotation_class_to_angle=rotation_angles, - ).with_queue(None), evt - -# }}} - # vim: filetype=pyopencl:fdm=marker -- GitLab From de73c274d31d0ce28f9b6059e9cce90ad9e12219 Mon Sep 17 00:00:00 2001 From: Isuru Fernando Date: Mon, 29 Jun 2020 18:56:21 -0500 Subject: [PATCH 04/11] Move vec_gcd to rcb --- boxtree/rotation_classes.py | 13 +++++++++++++ boxtree/translation_classes.py | 13 ------------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/boxtree/rotation_classes.py b/boxtree/rotation_classes.py index 54e8fa2..0f6955f 100644 --- a/boxtree/rotation_classes.py +++ b/boxtree/rotation_classes.py @@ -68,6 +68,19 @@ class RotationClassesInfo(DeviceDataRecord): class RotationClassesBuilder(TranslationClassesBuilder): + @staticmethod + def vec_gcd(vec): + """Return the GCD of a list of integers.""" + def gcd(a, b): + while b: + a, b = b, a % b + return a + + result = abs(vec[0]) + for elem in vec[1:]: + result = gcd(result, abs(elem)) + return result + def compute_rotation_classes(self, well_sep_is_n_away, dimensions, used_translation_classes): """Convert translation classes to a list of rotation classes and angles.""" diff --git a/boxtree/translation_classes.py b/boxtree/translation_classes.py index d5687fe..1f61751 100644 --- a/boxtree/translation_classes.py +++ b/boxtree/translation_classes.py @@ -242,19 +242,6 @@ class TranslationClassesBuilder(object): return _KernelInfo(translation_class_finder=translation_class_finder) - @staticmethod - def vec_gcd(vec): - """Return the GCD of a list of integers.""" - def gcd(a, b): - while b: - a, b = b, a % b - return a - - result = abs(vec[0]) - for elem in vec[1:]: - result = gcd(result, abs(elem)) - return result - @staticmethod def ntranslation_classes(well_sep_is_n_away, dimensions): return (4 * well_sep_is_n_away + 3) ** dimensions -- GitLab From f9ea485f1a9f8d35b1a4837b26bb49085a664685 Mon Sep 17 00:00:00 2001 From: Isuru Fernando Date: Mon, 29 Jun 2020 20:22:43 -0500 Subject: [PATCH 05/11] Remove unnecessary arg --- boxtree/translation_classes.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/boxtree/translation_classes.py b/boxtree/translation_classes.py index 1f61751..e339312 100644 --- a/boxtree/translation_classes.py +++ b/boxtree/translation_classes.py @@ -201,10 +201,7 @@ class TranslationClassesBuilder(object): """Build translation classes for List 2 translations. """ - def __init__(self, context, translation_class_per_level=True): - """translation_class_per_level determines whether the class depends on - the level or not. - """ + def __init__(self, context): self.context = context @memoize_method -- GitLab From c8c213660d5e3df6584029d242a2afe3f0f1a240 Mon Sep 17 00:00:00 2001 From: Isuru Fernando Date: Mon, 29 Jun 2020 20:50:31 -0500 Subject: [PATCH 06/11] Make tcb an attribute of rcb --- boxtree/rotation_classes.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/boxtree/rotation_classes.py b/boxtree/rotation_classes.py index 0f6955f..159debf 100644 --- a/boxtree/rotation_classes.py +++ b/boxtree/rotation_classes.py @@ -66,7 +66,11 @@ class RotationClassesInfo(DeviceDataRecord): return len(self.from_sep_siblings_rotation_class_to_angle) -class RotationClassesBuilder(TranslationClassesBuilder): +class RotationClassesBuilder(object): + + def __init__(self, context): + self.context = context + self.tcb = TranslationClassesBuilder(context) @staticmethod def vec_gcd(vec): @@ -88,7 +92,7 @@ class RotationClassesBuilder(TranslationClassesBuilder): angles = [] ntranslation_classes = ( - self.ntranslation_classes(well_sep_is_n_away, dimensions)) + self.tcb.ntranslation_classes(well_sep_is_n_away, dimensions)) translation_class_to_rot_class = ( np.empty(ntranslation_classes, dtype=np.int32)) @@ -96,7 +100,7 @@ class RotationClassesBuilder(TranslationClassesBuilder): translation_class_to_rot_class[:] = -1 for cls in used_translation_classes: - vec = self.translation_class_to_vector( + vec = self.tcb.translation_class_to_vector( well_sep_is_n_away, dimensions, cls) # Normalize the translation vector (by dividing by its GCD). @@ -134,7 +138,7 @@ class RotationClassesBuilder(TranslationClassesBuilder): """Returns a pair *info*, *evt* where info is a :class:`RotationClassesInfo`. """ evt, translation_class_is_used, translation_classes_lists = \ - self.compute_translation_classes(queue, trav, tree, wait_for, False) + self.tcb.compute_translation_classes(queue, trav, tree, wait_for, False) d = tree.dimensions n = trav.well_sep_is_n_away -- GitLab From 30ac2b64c1d4f3775ffc3e1de12f82b8eb08c9ca Mon Sep 17 00:00:00 2001 From: Isuru Fernando Date: Tue, 30 Jun 2020 06:17:57 +0200 Subject: [PATCH 07/11] Apply 8 suggestion(s) to 1 file(s) --- boxtree/translation_classes.py | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/boxtree/translation_classes.py b/boxtree/translation_classes.py index e339312..b5d4520 100644 --- a/boxtree/translation_classes.py +++ b/boxtree/translation_classes.py @@ -46,7 +46,7 @@ TRANSLATION_CLASS_FINDER_PREAMBLE_TEMPLATE = Template(r"""//CL:mako// // Return an integer vector indicating the a translation direction // as a multiple of the box diameter. - inline int_coord_vec_t get_translation_vector( + inline int_coord_vec_t get_normalized_translation_vector( coord_t root_extent, int level, coord_vec_t source_center, @@ -65,6 +65,8 @@ TRANSLATION_CLASS_FINDER_PREAMBLE_TEMPLATE = Template(r"""//CL:mako// // a dense range of integers [0, ..., (4*n+3)^d - 1], where // d is the dimension and n is well_sep_is_n_away. // + // The translation vector should be normalized for a box diameter of 1. + // // This relies on the fact that the entries of the vector will // always be in the range [-2n-1,...,2n+1]. // @@ -137,7 +139,7 @@ TRANSLATION_CLASS_FINDER_TEMPLATE = ElementwiseTemplate( ${load_center("source_center", "source_box_id")} ${load_center("target_center", "target_box_id")} - int_coord_vec_t vec = get_translation_vector( + int_coord_vec_t vec = get_normalized_translation_vector( root_extent, box_levels[source_box_id], source_center, target_center); int translation_class = get_translation_class(vec, well_sep_is_n_away); @@ -186,10 +188,10 @@ class TranslationClassesInfo(DeviceDataRecord): .. attribute:: from_sep_siblings_translation_classes_level_starts - ``coord_t [nlevels + 1]`` + ``int32 [nlevels + 1]`` A list with an entry for each level giving the starting translation - class id for that level. + class id for that level. Translation classes are numbered contiguously by level. """ @property @@ -240,14 +242,15 @@ class TranslationClassesBuilder(object): return _KernelInfo(translation_class_finder=translation_class_finder) @staticmethod - def ntranslation_classes(well_sep_is_n_away, dimensions): + def ntranslation_classes_per_level(well_sep_is_n_away, dimensions): return (4 * well_sep_is_n_away + 3) ** dimensions @staticmethod - def translation_class_to_vector(well_sep_is_n_away, dimensions, cls): + def translation_class_to_normalized_vector(well_sep_is_n_away, dimensions, cls): # This computes the vector for the translation class, using the inverse # of the formula found in get_translation_class() defined in # TRANSLATION_CLASS_FINDER_PREAMBLE_TEMPLATE. + assert 0 <= cls < self.ntranslation_classes_per_level(well_sep_is_n_away, dimensions) result = np.zeros(dimensions, dtype=np.int32) shift = 2 * well_sep_is_n_away + 1 base = 4 * well_sep_is_n_away + 3 -- GitLab From 0f54b8f1deff4d0f71c2474ff05e7132748e23a2 Mon Sep 17 00:00:00 2001 From: Isuru Fernando Date: Mon, 29 Jun 2020 23:35:49 -0500 Subject: [PATCH 08/11] Fix partial refactoring --- boxtree/rotation_classes.py | 9 +++++---- boxtree/translation_classes.py | 30 +++++++++++++++++------------- 2 files changed, 22 insertions(+), 17 deletions(-) diff --git a/boxtree/rotation_classes.py b/boxtree/rotation_classes.py index 159debf..63e6408 100644 --- a/boxtree/rotation_classes.py +++ b/boxtree/rotation_classes.py @@ -91,16 +91,17 @@ class RotationClassesBuilder(object): angle_to_rot_class = {} angles = [] - ntranslation_classes = ( - self.tcb.ntranslation_classes(well_sep_is_n_away, dimensions)) + ntranslation_classes_per_level = ( + self.tcb.ntranslation_classes_per_level(well_sep_is_n_away, + dimensions)) translation_class_to_rot_class = ( - np.empty(ntranslation_classes, dtype=np.int32)) + np.empty(ntranslation_classes_per_level, dtype=np.int32)) translation_class_to_rot_class[:] = -1 for cls in used_translation_classes: - vec = self.tcb.translation_class_to_vector( + vec = self.tcb.translation_class_to_normalized_vector( well_sep_is_n_away, dimensions, cls) # Normalize the translation vector (by dividing by its GCD). diff --git a/boxtree/translation_classes.py b/boxtree/translation_classes.py index b5d4520..18d802a 100644 --- a/boxtree/translation_classes.py +++ b/boxtree/translation_classes.py @@ -152,7 +152,8 @@ TRANSLATION_CLASS_FINDER_TEMPLATE = ElementwiseTemplate( } % if translation_class_per_level: - translation_class += box_levels[source_box_id] * ${ntranslation_classes}; + translation_class += box_levels[source_box_id] * \ + ${ntranslation_classes_per_level}; % endif translation_classes[i] = translation_class; @@ -184,14 +185,16 @@ class TranslationClassesInfo(DeviceDataRecord): ``coord_vec_t [nfrom_sep_siblings_translation_classes]`` Maps translation classes in *from_sep_siblings_translation_classes* - to the distance between the centers + to distance (translation) vectors from source box center to + target box center. .. attribute:: from_sep_siblings_translation_classes_level_starts ``int32 [nlevels + 1]`` A list with an entry for each level giving the starting translation - class id for that level. Translation classes are numbered contiguously by level. + class id for that level. Translation classes are numbered contiguously + by level. """ @property @@ -213,7 +216,7 @@ class TranslationClassesBuilder(object): int_coord_vec_dtype = cl.cltypes.vec_types[np.dtype(np.int32), dimensions] num_translation_classes = \ - self.ntranslation_classes(well_sep_is_n_away, dimensions) + self.ntranslation_classes_per_level(well_sep_is_n_away, dimensions) # Make sure translation classes can fit inside a 32 bit integer. if not num_translation_classes <= 1 + np.iinfo(np.int32).max: @@ -234,7 +237,7 @@ class TranslationClassesBuilder(object): ), var_values=( ("dimensions", dimensions), - ("ntranslation_classes", num_translation_classes), + ("ntranslation_classes_per_level", num_translation_classes), ("translation_class_per_level", translation_class_per_level), ), more_preamble=preamble)) @@ -245,12 +248,13 @@ class TranslationClassesBuilder(object): def ntranslation_classes_per_level(well_sep_is_n_away, dimensions): return (4 * well_sep_is_n_away + 3) ** dimensions - @staticmethod - def translation_class_to_normalized_vector(well_sep_is_n_away, dimensions, cls): + def translation_class_to_normalized_vector(self, well_sep_is_n_away, + dimensions, cls): # This computes the vector for the translation class, using the inverse # of the formula found in get_translation_class() defined in # TRANSLATION_CLASS_FINDER_PREAMBLE_TEMPLATE. - assert 0 <= cls < self.ntranslation_classes_per_level(well_sep_is_n_away, dimensions) + assert 0 <= cls < self.ntranslation_classes_per_level(well_sep_is_n_away, + dimensions) result = np.zeros(dimensions, dtype=np.int32) shift = 2 * well_sep_is_n_away + 1 base = 4 * well_sep_is_n_away + 3 @@ -277,7 +281,7 @@ class TranslationClassesBuilder(object): tree.box_level_dtype, coord_dtype, is_translation_per_level) ntranslation_classes = ( - self.ntranslation_classes(well_sep_is_n_away, dimensions)) + self.ntranslation_classes_per_level(well_sep_is_n_away, dimensions)) if is_translation_per_level: ntranslation_classes = ntranslation_classes * tree.nlevels @@ -330,8 +334,8 @@ class TranslationClassesBuilder(object): distances = np.empty((dimensions, len(translation_class_is_used)), dtype=tree.coord_dtype) - num_translation_classes = self.ntranslation_classes(well_sep_is_n_away, - dimensions) + num_translation_classes = \ + self.ntranslation_classes_per_level(well_sep_is_n_away, dimensions) nlevels = tree.nlevels count = 0 @@ -349,8 +353,8 @@ class TranslationClassesBuilder(object): continue used_translation_classes_map[i] = count - unit_vector = self.translation_class_to_vector(well_sep_is_n_away, - dimensions, cls_without_level) + unit_vector = self.translation_class_to_normalized_vector( + well_sep_is_n_away, dimensions, cls_without_level) distances[:, count] = unit_vector * tree.root_extent / (1 << level) count = count + 1 -- GitLab From 5571926c5a22e790839aaef03f80e685a190e2de Mon Sep 17 00:00:00 2001 From: Isuru Fernando Date: Wed, 1 Jul 2020 23:22:25 +0200 Subject: [PATCH 09/11] Apply 1 suggestion(s) to 1 file(s) --- test/test_traversal.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/test_traversal.py b/test/test_traversal.py index 64b21ed..a0752b6 100644 --- a/test/test_traversal.py +++ b/test/test_traversal.py @@ -361,7 +361,7 @@ def test_plot_traversal(ctx_factory, well_sep_is_n_away=1, plot=False): # {{{ test_from_sep_siblings_translation_classes @pytest.mark.parametrize("well_sep_is_n_away", (1, 2)) -def test_from_sep_siblings_translation_classes(ctx_factory, well_sep_is_n_away): +def test_from_sep_siblings_translation_and_rotation_classes(ctx_factory, well_sep_is_n_away): ctx = ctx_factory() queue = cl.CommandQueue(ctx) -- GitLab From eb08c8f5794068bf45478de1de3b45ed23abbd22 Mon Sep 17 00:00:00 2001 From: Isuru Fernando Date: Wed, 1 Jul 2020 16:23:59 -0500 Subject: [PATCH 10/11] Fix formatting --- test/test_traversal.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/test_traversal.py b/test/test_traversal.py index a0752b6..81913a1 100644 --- a/test/test_traversal.py +++ b/test/test_traversal.py @@ -361,7 +361,8 @@ def test_plot_traversal(ctx_factory, well_sep_is_n_away=1, plot=False): # {{{ test_from_sep_siblings_translation_classes @pytest.mark.parametrize("well_sep_is_n_away", (1, 2)) -def test_from_sep_siblings_translation_and_rotation_classes(ctx_factory, well_sep_is_n_away): +def test_from_sep_siblings_translation_and_rotation_classes(ctx_factory, + well_sep_is_n_away): ctx = ctx_factory() queue = cl.CommandQueue(ctx) -- GitLab From 786f4b0721ab295ae68a2e4f183c30da9ef9d79c Mon Sep 17 00:00:00 2001 From: Isuru Fernando Date: Thu, 2 Jul 2020 00:06:49 +0200 Subject: [PATCH 11/11] Apply 1 suggestion(s) to 1 file(s) --- test/test_traversal.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/test_traversal.py b/test/test_traversal.py index 81913a1..ec44938 100644 --- a/test/test_traversal.py +++ b/test/test_traversal.py @@ -358,7 +358,7 @@ def test_plot_traversal(ctx_factory, well_sep_is_n_away=1, plot=False): # }}} -# {{{ test_from_sep_siblings_translation_classes +# {{{ test_from_sep_siblings_translation_and_rotation_classes @pytest.mark.parametrize("well_sep_is_n_away", (1, 2)) def test_from_sep_siblings_translation_and_rotation_classes(ctx_factory, -- GitLab