Skip to content

Commit 0559bae

Browse files
add Partition_N
1 parent b4c4d87 commit 0559bae

File tree

8 files changed

+192
-76
lines changed

8 files changed

+192
-76
lines changed

CHANGELOG.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,18 +48,18 @@ All notable changes to this project will be documented in this file.
4848
- `task_based_internal_add_hmatrix_hmatrix_product` for task based alternative to `{sequential,openmp}_internal_add_hmatrix_hmatrix_product`.
4949
- `task_based_internal_triangular_hmatrix_hmatrix_solve` for task based alternative to `internal_triangular_hmatrix_hmatrix_solve`.
5050
- `task_based_lu_factorization` and `task_based_cholesky_factorization` for task based alternatives to `{sequential,openmp}_lu_factorization` and `{sequential,openmp}_cholesky_factorization`.
51-
- `test_task_based_hmatrix_***.hpp` for testing various task based features.
51+
- `test_task_based_hmatrix_*.hpp` for testing various task based features.
5252
- `internal_add_lrmat_hmatrix` is now overloaded to handle the case where the HMatrix is larger than the LowRankMatrix.
5353
- `get_leaves_from` is overloaded to return non const arguments.
5454
- `get_false_positive` in a tree builder.
5555
- `left_hmatrix_ancestor_of_right_hmatrix` and `left_hmatrix_descendant_of_right_hmatrix` for returning parent and children of a hmatrix.
56+
- `Partition_N` is an alternative to `Partition` for defining the partition of a cluster. The latter only splits along the principal axis of the cluster, while the former tries to be smarter.
5657

5758
### Changed
5859

5960
- `VirtualInternalLowRankGenerator` and `VirtualLowRankGenerator`'s `copy_low_rank_approximation` function takes a `LowRankMatrix` as input to populate it and returns a boolean. The return value is true if the compression succeded, false otherwise.
6061
- `LowRankMatrix` constructors changed. It only takes sizes and an epsilon or a required rank. Then, it is expected to call a `VirtualInternalLowRankGenerator` to populate it.
6162
- `ClusterTreeBuilder` has now one strategy as `VirtualPartitioning`. Usual implementations are still available, for example using `Partitioning<double,ComputeLargestExtent,RegularSplitting>`.
62-
- When using `ClusterTreeBuilder` with `number_of_children=2^spatial_dimension`, it will do a binary/quad/octo-tree instead of `number_of_children` cut along the main direction.
6363
- `ClusterTreeBuilder` parameter `minclustersize` was removed, and a parameter `maximal_leaf_size` has been added.
6464
- `DistributedOperator` supports now both "global-to-local" and "local-to-local" operators, using respectively `VirtualGlobalToLocalOperator` and `VirtualLocalToLocalOperator` interfaces. The linear algebra associated has been updated to follow a more Blas-like interface.
6565
- `MatrixView` has been added to ease the use of matrix product. Most public functions for matrix products are also new templated to accept, `Matrix`, `MatrixView` or any other type following the same interface.

include/htool/clustering/implementations/partitioning.hpp

Lines changed: 131 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -20,48 +20,129 @@ class Partitioning : public VirtualPartitioning<CoordinatePrecision> {
2020

2121
// Compute directions
2222
Matrix<CoordinatePrecision> directions;
23-
directions = ComputationDirectionPolicy::compute_direction(current_cluster, spatial_dimension, coordinates, radii, weights);
24-
25-
// If number of partitions corresponds to 2^d, we do binary/quadtree/octree/...
26-
if (number_of_partitions == pow(2, spatial_dimension)) {
27-
std::vector<int> dimensions(spatial_dimension);
28-
std::iota(dimensions.begin(), dimensions.end(), int(0));
29-
std::stack<std::tuple<int, int, int>> stack;
30-
stack.push(std::make_tuple(current_cluster.get_offset(), current_cluster.get_size(), 0));
31-
std::vector<std::pair<int, int>> result;
32-
while (!stack.empty()) {
33-
auto tmp_partition = stack.top();
34-
stack.pop();
35-
auto tmp_offset = std::get<0>(tmp_partition);
36-
auto tmp_size = std::get<1>(tmp_partition);
37-
auto tmp_dimension = std::get<2>(tmp_partition);
38-
39-
std::vector<CoordinatePrecision> direction = get_col(directions, tmp_dimension);
40-
41-
std::sort(permutation.begin() + tmp_offset, permutation.begin() + tmp_offset + tmp_size, [&](int a, int b) {
42-
CoordinatePrecision c = std::inner_product(coordinates + spatial_dimension * a, coordinates + spatial_dimension * (1 + a), direction.data(), CoordinatePrecision(0));
43-
CoordinatePrecision d = std::inner_product(coordinates + spatial_dimension * b, coordinates + spatial_dimension * (1 + b), direction.data(), CoordinatePrecision(0));
44-
return c < d;
45-
});
23+
std::vector<CoordinatePrecision> directions_weights;
24+
std::tie(directions, directions_weights) = ComputationDirectionPolicy::compute_direction(current_cluster, spatial_dimension, coordinates, radii, weights);
4625

47-
auto tmp = SplittingPolicy::splitting(tmp_offset, tmp_size, spatial_dimension, coordinates, current_cluster.get_permutation(), direction, 2);
26+
// Sort along main direction
27+
std::sort(permutation.begin() + current_offset, permutation.begin() + current_offset + current_size, [&](int a, int b) {
28+
CoordinatePrecision c = std::inner_product(coordinates + spatial_dimension * a, coordinates + spatial_dimension * (1 + a), directions.data(), CoordinatePrecision(0));
29+
CoordinatePrecision d = std::inner_product(coordinates + spatial_dimension * b, coordinates + spatial_dimension * (1 + b), directions.data(), CoordinatePrecision(0));
30+
return c < d;
31+
});
4832

49-
if ((tmp_dimension < spatial_dimension - 1) and tmp.size() == 2) {
50-
stack.push(std::make_tuple(tmp[1].first, tmp[1].second, tmp_dimension + 1));
51-
stack.push(std::make_tuple(tmp[0].first, tmp[0].second, tmp_dimension + 1));
52-
} else if ((tmp_dimension == spatial_dimension - 1) and tmp.size() == 2) {
53-
result.insert(result.end(), tmp.begin(), tmp.end());
54-
} else {
55-
break;
56-
}
33+
// Split along main direction
34+
return SplittingPolicy::splitting(current_cluster.get_offset(), current_cluster.get_size(), spatial_dimension, coordinates, current_cluster.get_permutation(), get_col(directions, 0), number_of_partitions);
35+
}
36+
};
37+
38+
template <typename CoordinatePrecision, typename ComputationDirectionPolicy, typename SplittingPolicy>
39+
class Partitioning_N : public VirtualPartitioning<CoordinatePrecision> {
40+
41+
// Generate all ordered remaining_d-decomposition of remaining_n
42+
void backtrack(int remaining_n, int remaining_d, int start, std::vector<int> &current, std::vector<std::vector<int>> &results) {
43+
if (remaining_d == 1) {
44+
if (remaining_n <= start && remaining_n >= 1) {
45+
current.push_back(remaining_n);
46+
results.push_back(current);
47+
current.pop_back();
48+
}
49+
return;
50+
}
51+
52+
for (int f = start; f >= 1; f--) {
53+
if (remaining_n % f == 0) {
54+
current.push_back(f);
55+
backtrack(remaining_n / f, remaining_d - 1, f, current, results);
56+
current.pop_back();
57+
}
58+
}
59+
}
60+
61+
CoordinatePrecision aspect_ratio(std::vector<CoordinatePrecision>) {
62+
}
63+
64+
std::vector<int> distributed_splittings(int number_of_dimension, int number_of_partitions, std::vector<CoordinatePrecision> weights) {
65+
// Generate all ordered integer decompositions of number_of_dimension elements
66+
std::vector<std::vector<int>> integer_decompositions;
67+
std::vector<int> current;
68+
backtrack(number_of_partitions, number_of_dimension, number_of_partitions, current, integer_decompositions);
69+
70+
// Pick best decomposition for aspect ratio
71+
CoordinatePrecision cost = std::numeric_limits<CoordinatePrecision>::max();
72+
int index = 0;
73+
int result_index;
74+
std::vector<CoordinatePrecision> aspect_ratios(number_of_dimension);
75+
for (auto &integer_decomposition : integer_decompositions) {
76+
std::transform(integer_decomposition.cbegin(), integer_decomposition.cend(), weights.cbegin(), aspect_ratios.begin(), [](const CoordinatePrecision &p, const CoordinatePrecision &a) { return a / p; });
77+
CoordinatePrecision current_cost = *std::max_element(aspect_ratios.begin(), aspect_ratios.end()) / *std::min_element(aspect_ratios.begin(), aspect_ratios.end());
78+
79+
if (current_cost < cost) {
80+
cost = current_cost;
81+
result_index = index;
82+
}
83+
index++;
84+
}
85+
return integer_decompositions[result_index];
86+
}
87+
88+
public:
89+
std::vector<std::pair<int, int>> compute_partitioning(Cluster<CoordinatePrecision> &current_cluster, int spatial_dimension, const CoordinatePrecision *coordinates, const CoordinatePrecision *const radii, const CoordinatePrecision *const weights, int number_of_partitions) override {
90+
91+
std::vector<int> &permutation = current_cluster.get_permutation();
92+
auto current_offset = current_cluster.get_offset();
93+
auto current_size = current_cluster.get_size();
94+
95+
// Compute directions
96+
Matrix<CoordinatePrecision> directions;
97+
std::vector<CoordinatePrecision> directions_weights;
98+
std::tie(directions, directions_weights) = ComputationDirectionPolicy::compute_direction(current_cluster, spatial_dimension, coordinates, radii, weights);
99+
100+
int number_of_relevant_directions = 0;
101+
for (auto direction_weight : directions_weights) {
102+
if (direction_weight > std::numeric_limits<CoordinatePrecision>::epsilon() * 10) {
103+
number_of_relevant_directions += 1;
57104
}
105+
}
106+
107+
std::vector<int> number_of_splittings_by_direction = distributed_splittings(number_of_relevant_directions, number_of_partitions, directions_weights);
108+
number_of_relevant_directions = number_of_splittings_by_direction.size();
109+
110+
std::stack<std::tuple<int, int, int>> stack;
111+
stack.push(std::make_tuple(current_cluster.get_offset(), current_cluster.get_size(), 0));
112+
std::vector<std::pair<int, int>> result;
113+
while (!stack.empty()) {
114+
auto tmp_partition = stack.top();
115+
stack.pop();
116+
auto tmp_offset = std::get<0>(tmp_partition);
117+
auto tmp_size = std::get<1>(tmp_partition);
118+
auto tmp_dimension = std::get<2>(tmp_partition);
119+
120+
std::vector<CoordinatePrecision> direction = get_col(directions, tmp_dimension);
58121

59-
if (result.size() == number_of_partitions) {
60-
std::sort(result.begin(), result.end(), [](auto a, auto b) { return a.first < b.first; });
61-
return result;
122+
std::sort(permutation.begin() + tmp_offset, permutation.begin() + tmp_offset + tmp_size, [&](int a, int b) {
123+
CoordinatePrecision c = std::inner_product(coordinates + spatial_dimension * a, coordinates + spatial_dimension * (1 + a), direction.data(), CoordinatePrecision(0));
124+
CoordinatePrecision d = std::inner_product(coordinates + spatial_dimension * b, coordinates + spatial_dimension * (1 + b), direction.data(), CoordinatePrecision(0));
125+
return c < d;
126+
});
127+
128+
auto tmp = SplittingPolicy::splitting(tmp_offset, tmp_size, spatial_dimension, coordinates, current_cluster.get_permutation(), direction, number_of_splittings_by_direction[tmp_dimension]);
129+
130+
if ((tmp_dimension < number_of_relevant_directions - 1) and tmp.size() == number_of_splittings_by_direction[tmp_dimension]) {
131+
for (int p = number_of_splittings_by_direction[tmp_dimension] - 1; p >= 0; p--) {
132+
stack.push(std::make_tuple(tmp[p].first, tmp[p].second, tmp_dimension + 1));
133+
}
134+
} else if ((tmp_dimension == number_of_relevant_directions - 1) and tmp.size() == number_of_splittings_by_direction[tmp_dimension]) {
135+
result.insert(result.end(), tmp.begin(), tmp.end());
136+
} else {
137+
break;
62138
}
63139
}
64140

141+
if (result.size() == number_of_partitions) {
142+
std::sort(result.begin(), result.end(), [](auto a, auto b) { return a.first < b.first; });
143+
return result;
144+
}
145+
65146
// Sort along main direction
66147
std::sort(permutation.begin() + current_offset, permutation.begin() + current_offset + current_size, [&](int a, int b) {
67148
CoordinatePrecision c = std::inner_product(coordinates + spatial_dimension * a, coordinates + spatial_dimension * (1 + a), directions.data(), CoordinatePrecision(0));
@@ -77,14 +158,13 @@ class Partitioning : public VirtualPartitioning<CoordinatePrecision> {
77158
template <typename T>
78159
class ComputeLargestExtent {
79160
public:
80-
static Matrix<T> compute_direction(const Cluster<T> &cluster, int spatial_dimension, const T *const coordinates, const T *const, const T *const weights) {
161+
static std::pair<Matrix<T>, std::vector<T>> compute_direction(const Cluster<T> &cluster, int spatial_dimension, const T *const coordinates, const T *const, const T *const weights) {
81162
if (spatial_dimension != 2 && spatial_dimension != 3) {
82163
htool::Logger::get_instance().log(LogLevel::ERROR, "clustering not define for spatial dimension !=2 and !=3"); // LCOV_EXCL_LINE
83164
}
84165

85166
const std::vector<int> &permutation = cluster.get_permutation();
86167
Matrix<T> cov(spatial_dimension, spatial_dimension);
87-
Matrix<T> directions(spatial_dimension, spatial_dimension);
88168

89169
for (int j = 0; j < cluster.get_size(); j++) {
90170
std::vector<T> u(spatial_dimension, 0);
@@ -98,19 +178,23 @@ class ComputeLargestExtent {
98178
}
99179
}
100180
}
101-
if (spatial_dimension == 2) {
102-
directions = solve_EVP_2(cov);
103-
} else if (spatial_dimension == 3) {
104-
directions = solve_EVP_3(cov);
181+
182+
auto evp_eigs = spatial_dimension == 2 ? solve_EVP_2(cov) : solve_EVP_3(cov);
183+
for (auto &eig : evp_eigs.second) {
184+
if (eig > 0) {
185+
eig = std::sqrt(eig);
186+
} else {
187+
eig = 0;
188+
}
105189
}
106-
return directions;
190+
return evp_eigs;
107191
}
108192
};
109193

110194
template <typename T>
111195
class ComputeBoundingBox {
112196
public:
113-
static Matrix<T> compute_direction(const Cluster<T> &cluster, int spatial_dimension, const T *const coordinates, const T *const, const T *const) {
197+
static std::pair<Matrix<T>, std::vector<T>> compute_direction(const Cluster<T> &cluster, int spatial_dimension, const T *const coordinates, const T *const, const T *const) {
114198

115199
const std::vector<int> &permutation = cluster.get_permutation();
116200

@@ -131,14 +215,17 @@ class ComputeBoundingBox {
131215
}
132216

133217
// Direction of largest extent
218+
std::vector<T> lengths(spatial_dimension);
134219
std::vector<int> indexes(spatial_dimension);
135220
std::iota(indexes.begin(), indexes.end(), int(0));
136221
std::sort(indexes.begin(), indexes.end(), [&max_point, &min_point](int a, int b) { return (max_point[a] - min_point[a]) < (max_point[b] - min_point[b]); });
137222
Matrix<T> directions(spatial_dimension, spatial_dimension);
138223
for (int dim = 0; dim < spatial_dimension; dim++) {
139224
directions(indexes[spatial_dimension - 1 - dim], dim) = 1;
225+
226+
lengths[dim] = max_point[indexes[spatial_dimension - 1 - dim]] - min_point[indexes[spatial_dimension - 1 - dim]];
140227
}
141-
return directions;
228+
return std::make_pair(directions, lengths);
142229
}
143230
};
144231

include/htool/misc/evp.hpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
namespace htool {
1111

1212
template <typename T>
13-
Matrix<T> solve_EVP_2(const Matrix<T> &cov) {
13+
std::pair<Matrix<T>, std::vector<T>> solve_EVP_2(const Matrix<T> &cov) {
1414
std::vector<T> dir(2, 0);
1515
std::vector<T> eigs(2);
1616
Matrix<T> I(2, 2);
@@ -45,11 +45,11 @@ Matrix<T> solve_EVP_2(const Matrix<T> &cov) {
4545
result(0, 0) = 1;
4646
result(1, 1) = 1;
4747
}
48-
return result;
48+
return std::make_pair(result, eigs);
4949
}
5050

5151
template <typename T>
52-
Matrix<T> solve_EVP_3(const Matrix<T> &cov) {
52+
std::pair<Matrix<T>, std::vector<T>> solve_EVP_3(const Matrix<T> &cov) {
5353
Matrix<T> result(3, 3);
5454
T p1 = std::pow(cov(0, 1), 2) + std::pow(cov(0, 2), 2) + std::pow(cov(1, 2), 2);
5555
std::vector<T> eigs(3);
@@ -150,7 +150,7 @@ Matrix<T> solve_EVP_3(const Matrix<T> &cov) {
150150
result(2, 2) = 1;
151151
}
152152
}
153-
return result;
153+
return std::make_pair(result, eigs);
154154
}
155155
} // namespace htool
156156
#endif

include/htool/testing/geometry.hpp

Lines changed: 24 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,25 +8,40 @@
88
namespace htool {
99

1010
template <typename T>
11-
void create_disk(int space_dim, T z, int nr, T *const xt) {
12-
11+
void create_rotated_ellipse(int space_dim, T a, T b, T alpha, T z, int nr, T *const xt) {
1312
std::mt19937 mersenne_engine(0);
1413
std::uniform_real_distribution<T> dist(0, 1);
1514
auto gen = [&dist, &mersenne_engine]() {
1615
return dist(mersenne_engine);
1716
};
18-
T z1 = z;
17+
18+
T cos_alpha = std::cos(alpha);
19+
T sin_alpha = std::sin(alpha);
20+
1921
for (int j = 0; j < nr; j++) {
20-
T rho = gen(); // (T) otherwise integer division!
21-
T theta = gen();
22-
xt[space_dim * j + 0] = std::sqrt(rho) * std::cos(2 * static_cast<T>(M_PI) * theta);
23-
xt[space_dim * j + 1] = std::sqrt(rho) * std::sin(2 * static_cast<T>(M_PI) * theta);
22+
T rho = gen();
23+
T theta = gen();
24+
T r = std::sqrt(rho);
25+
T phi = 2 * static_cast<T>(M_PI) * theta;
26+
27+
// Axis-aligned ellipse coordinates
28+
T x_prime = a * r * std::cos(phi);
29+
T y_prime = b * r * std::sin(phi);
30+
31+
// Apply rotation
32+
xt[space_dim * j + 0] = cos_alpha * x_prime - sin_alpha * y_prime;
33+
xt[space_dim * j + 1] = sin_alpha * x_prime + cos_alpha * y_prime;
34+
2435
if (space_dim == 3)
25-
xt[space_dim * j + 2] = z1;
26-
// sqrt(rho) otherwise the points would be concentrated in the center of the disk
36+
xt[space_dim * j + 2] = z;
2737
}
2838
}
2939

40+
template <typename T>
41+
void create_disk(int space_dim, T z, int nr, T *const xt) {
42+
create_rotated_ellipse(space_dim, T(1.), T(1.), T(0.), z, nr, xt);
43+
}
44+
3045
template <typename T>
3146
void create_sphere(int nr, T *const xt, std::array<T, 3> offset = {0, 0, 0}) {
3247

include/htool/testing/partition.hpp

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ template <typename CoordinatePrecision>
1414
std::vector<int> test_global_partition(int spatial_dimension, int number_of_points, const std::vector<CoordinatePrecision> &coordinates, int partition_size) {
1515
// Compute largest extent
1616
Matrix<CoordinatePrecision> direction(spatial_dimension, spatial_dimension);
17+
std::vector<CoordinatePrecision> weigths;
1718
Matrix<CoordinatePrecision> cov(spatial_dimension, spatial_dimension);
1819
std::vector<int> permutation(number_of_points, 0);
1920
std::iota(permutation.begin(), permutation.end(), int(0));
@@ -31,9 +32,9 @@ std::vector<int> test_global_partition(int spatial_dimension, int number_of_poin
3132
}
3233
}
3334
if (spatial_dimension == 2) {
34-
direction = solve_EVP_2(cov);
35+
std::tie(direction, weigths) = solve_EVP_2(cov);
3536
} else if (spatial_dimension == 3) {
36-
direction = solve_EVP_3(cov);
37+
std::tie(direction, weigths) = solve_EVP_3(cov);
3738
}
3839

3940
// sort
@@ -62,6 +63,7 @@ template <typename CoordinatePrecision>
6263
std::vector<int> test_local_partition(int spatial_dimension, int number_of_points, std::vector<CoordinatePrecision> &coordinates, int partition_size) {
6364
// Compute largest extent
6465
Matrix<CoordinatePrecision> direction(spatial_dimension, spatial_dimension);
66+
std::vector<CoordinatePrecision> weigths;
6567
Matrix<CoordinatePrecision> cov(spatial_dimension, spatial_dimension);
6668
std::vector<int> permutation(number_of_points, 0);
6769
std::iota(permutation.begin(), permutation.end(), int(0));
@@ -79,9 +81,9 @@ std::vector<int> test_local_partition(int spatial_dimension, int number_of_point
7981
}
8082
}
8183
if (spatial_dimension == 2) {
82-
direction = solve_EVP_2(cov);
84+
std::tie(direction, weigths) = solve_EVP_2(cov);
8385
} else if (spatial_dimension == 3) {
84-
direction = solve_EVP_3(cov);
86+
std::tie(direction, weigths) = solve_EVP_3(cov);
8587
}
8688

8789
// sort

0 commit comments

Comments
 (0)