gdsdiff 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- gdsdiff/__init__.py +148 -0
- gdsdiff/__main__.py +7 -0
- gdsdiff/_environment.py +12 -0
- gdsdiff/_version.py +34 -0
- gdsdiff/acceptance_evidence.py +530 -0
- gdsdiff/api.py +2955 -0
- gdsdiff/backends/__init__.py +26 -0
- gdsdiff/backends/base.py +77 -0
- gdsdiff/backends/boundary_loop_ctypes.py +191 -0
- gdsdiff/backends/cpu.py +20 -0
- gdsdiff/backends/cpu_ctypes.py +255 -0
- gdsdiff/backends/cuda.py +40 -0
- gdsdiff/backends/cuda_ctypes.py +1297 -0
- gdsdiff/backends/exact.py +424 -0
- gdsdiff/backends/geometry_ctypes.py +252 -0
- gdsdiff/backends/identity.py +53 -0
- gdsdiff/backends/metal.py +198 -0
- gdsdiff/backends/metal_ctypes.py +866 -0
- gdsdiff/backends/polygon_extract_ctypes.py +233 -0
- gdsdiff/backends/structural_ctypes.py +130 -0
- gdsdiff/boundary_loops.py +616 -0
- gdsdiff/cache.py +544 -0
- gdsdiff/canonicalize.py +176 -0
- gdsdiff/cli.py +352 -0
- gdsdiff/config.py +257 -0
- gdsdiff/cuda_setup.py +174 -0
- gdsdiff/design_history.py +65 -0
- gdsdiff/diagnostics.py +42 -0
- gdsdiff/diff_gds.py +66 -0
- gdsdiff/exact_diff_markdown.py +979 -0
- gdsdiff/exact_oracle.py +649 -0
- gdsdiff/extract.py +376 -0
- gdsdiff/gds_metadata.py +43 -0
- gdsdiff/geometry.py +112 -0
- gdsdiff/grid.py +143 -0
- gdsdiff/hierarchy.py +215 -0
- gdsdiff/hierarchy_extract.py +263 -0
- gdsdiff/inventory.py +153 -0
- gdsdiff/multiprocessing_support.py +39 -0
- gdsdiff/native.py +135 -0
- gdsdiff/native_cache.py +265 -0
- gdsdiff/native_src/cpu_prefilter.cpp +349 -0
- gdsdiff/native_src/gds_boundary_loops.cpp +505 -0
- gdsdiff/native_src/gds_geometry_scan.cpp +601 -0
- gdsdiff/native_src/gds_polygon_extract.cpp +594 -0
- gdsdiff/native_src/gds_structural_scan.cpp +335 -0
- gdsdiff/native_src/gdsdiff/CMakeLists.txt +74 -0
- gdsdiff/native_src/gdsdiff/core.cpp +191 -0
- gdsdiff/native_src/gdsdiff/core.hpp +96 -0
- gdsdiff/native_src/gdsdiff/cuda_assignment.cu +304 -0
- gdsdiff/native_src/gdsdiff/cuda_assignment.cuh +33 -0
- gdsdiff/native_src/gdsdiff/cuda_candidates.cu +133 -0
- gdsdiff/native_src/gdsdiff/cuda_candidates.cuh +17 -0
- gdsdiff/native_src/gdsdiff/cuda_ctypes.cu +4528 -0
- gdsdiff/native_src/gdsdiff/cuda_memory.cu +194 -0
- gdsdiff/native_src/gdsdiff/cuda_memory.cuh +34 -0
- gdsdiff/native_src/gdsdiff/metal_ctypes.mm +2791 -0
- gdsdiff/native_src/gdsdiff/python_bindings.cpp +21 -0
- gdsdiff/native_src/gdsdiff/test_core.cpp +93 -0
- gdsdiff/native_src/gdsdiff/test_cuda_assignment.cu +109 -0
- gdsdiff/native_src/gdsdiff/test_cuda_candidates.cu +94 -0
- gdsdiff/native_src/gdsdiff/test_cuda_memory.cu +97 -0
- gdsdiff/policy.py +305 -0
- gdsdiff/polygon_components.py +860 -0
- gdsdiff/profiles.py +152 -0
- gdsdiff/py.typed +1 -0
- gdsdiff/rect_coverage.py +384 -0
- gdsdiff/report.py +354 -0
- gdsdiff/schemas/gdsdiff_report-v1.schema.json +92 -0
- gdsdiff/semantics.py +75 -0
- gdsdiff/source_edge_diff.py +1120 -0
- gdsdiff/spatial.py +71 -0
- gdsdiff/structural_guard.py +161 -0
- gdsdiff/surplus_coverage.py +255 -0
- gdsdiff/synthetic_suite.py +1643 -0
- gdsdiff/templates.py +709 -0
- gdsdiff/tiling.py +153 -0
- gdsdiff/windowed_exact.py +270 -0
- gdsdiff/windows.py +184 -0
- gdsdiff-0.1.0.dist-info/METADATA +135 -0
- gdsdiff-0.1.0.dist-info/RECORD +85 -0
- gdsdiff-0.1.0.dist-info/WHEEL +5 -0
- gdsdiff-0.1.0.dist-info/entry_points.txt +4 -0
- gdsdiff-0.1.0.dist-info/licenses/LICENSE +674 -0
- gdsdiff-0.1.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,304 @@
|
|
|
1
|
+
#include "cuda_assignment.cuh"
|
|
2
|
+
|
|
3
|
+
#include <cuda_runtime.h>
|
|
4
|
+
|
|
5
|
+
#include <algorithm>
|
|
6
|
+
#include <numeric>
|
|
7
|
+
#include <stdexcept>
|
|
8
|
+
#include <string>
|
|
9
|
+
|
|
10
|
+
namespace gdsdiff {
|
|
11
|
+
namespace {
|
|
12
|
+
|
|
13
|
+
struct DevicePolygon {
|
|
14
|
+
std::int64_t x0;
|
|
15
|
+
std::int64_t y0;
|
|
16
|
+
std::int64_t x1;
|
|
17
|
+
std::int64_t y1;
|
|
18
|
+
std::int32_t layer;
|
|
19
|
+
std::int32_t datatype;
|
|
20
|
+
std::uint64_t canonical_id;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
struct DeviceTileAssignment {
|
|
24
|
+
std::int32_t layer;
|
|
25
|
+
std::int32_t datatype;
|
|
26
|
+
std::int64_t tx;
|
|
27
|
+
std::int64_t ty;
|
|
28
|
+
std::uint64_t canonical_id;
|
|
29
|
+
std::uint8_t side;
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
struct DeviceOversized {
|
|
33
|
+
std::int32_t layer;
|
|
34
|
+
std::int32_t datatype;
|
|
35
|
+
std::uint64_t canonical_id;
|
|
36
|
+
std::uint8_t side;
|
|
37
|
+
std::int64_t tx0;
|
|
38
|
+
std::int64_t ty0;
|
|
39
|
+
std::int64_t tx1;
|
|
40
|
+
std::int64_t ty1;
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
__device__ std::int64_t device_floor_div(std::int64_t value, std::int64_t divisor) {
|
|
44
|
+
std::int64_t quotient = value / divisor;
|
|
45
|
+
const std::int64_t remainder = value % divisor;
|
|
46
|
+
if (remainder != 0 && ((remainder < 0) != (divisor < 0))) {
|
|
47
|
+
--quotient;
|
|
48
|
+
}
|
|
49
|
+
return quotient;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
__global__ void count_kernel(
|
|
53
|
+
const DevicePolygon* polygons,
|
|
54
|
+
std::uint64_t n,
|
|
55
|
+
std::int64_t tile_size,
|
|
56
|
+
std::uint64_t max_tiles_per_polygon,
|
|
57
|
+
std::uint64_t* counts,
|
|
58
|
+
std::uint8_t* oversized_flags,
|
|
59
|
+
DeviceOversized* oversized,
|
|
60
|
+
std::uint8_t side,
|
|
61
|
+
std::uint32_t* error_flags) {
|
|
62
|
+
const std::uint64_t index = blockIdx.x * blockDim.x + threadIdx.x;
|
|
63
|
+
if (index >= n) {
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
const DevicePolygon polygon = polygons[index];
|
|
67
|
+
const std::int64_t tx0 = device_floor_div(polygon.x0, tile_size);
|
|
68
|
+
const std::int64_t ty0 = device_floor_div(polygon.y0, tile_size);
|
|
69
|
+
const std::int64_t tx1 = device_floor_div(polygon.x1, tile_size);
|
|
70
|
+
const std::int64_t ty1 = device_floor_div(polygon.y1, tile_size);
|
|
71
|
+
const std::int64_t span_x = tx1 - tx0 + 1;
|
|
72
|
+
const std::int64_t span_y = ty1 - ty0 + 1;
|
|
73
|
+
if (span_x <= 0 || span_y <= 0) {
|
|
74
|
+
error_flags[index] = 1;
|
|
75
|
+
counts[index] = 0;
|
|
76
|
+
oversized_flags[index] = 0;
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
const auto span_x_u = static_cast<std::uint64_t>(span_x);
|
|
80
|
+
const auto span_y_u = static_cast<std::uint64_t>(span_y);
|
|
81
|
+
if (span_y_u != 0 && span_x_u > UINT64_MAX / span_y_u) {
|
|
82
|
+
error_flags[index] = 2;
|
|
83
|
+
counts[index] = 0;
|
|
84
|
+
oversized_flags[index] = 0;
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
const std::uint64_t count = span_x_u * span_y_u;
|
|
88
|
+
if (count > max_tiles_per_polygon) {
|
|
89
|
+
counts[index] = 0;
|
|
90
|
+
oversized_flags[index] = 1;
|
|
91
|
+
oversized[index] = DeviceOversized{
|
|
92
|
+
polygon.layer,
|
|
93
|
+
polygon.datatype,
|
|
94
|
+
polygon.canonical_id,
|
|
95
|
+
side,
|
|
96
|
+
tx0,
|
|
97
|
+
ty0,
|
|
98
|
+
tx1,
|
|
99
|
+
ty1,
|
|
100
|
+
};
|
|
101
|
+
} else {
|
|
102
|
+
counts[index] = count;
|
|
103
|
+
oversized_flags[index] = 0;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
__global__ void emit_kernel(
|
|
108
|
+
const DevicePolygon* polygons,
|
|
109
|
+
std::uint64_t n,
|
|
110
|
+
std::int64_t tile_size,
|
|
111
|
+
const std::uint64_t* offsets,
|
|
112
|
+
const std::uint8_t* oversized_flags,
|
|
113
|
+
DeviceTileAssignment* assignments,
|
|
114
|
+
std::uint8_t side) {
|
|
115
|
+
const std::uint64_t index = blockIdx.x * blockDim.x + threadIdx.x;
|
|
116
|
+
if (index >= n || oversized_flags[index]) {
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
const DevicePolygon polygon = polygons[index];
|
|
120
|
+
const std::int64_t tx0 = device_floor_div(polygon.x0, tile_size);
|
|
121
|
+
const std::int64_t ty0 = device_floor_div(polygon.y0, tile_size);
|
|
122
|
+
const std::int64_t tx1 = device_floor_div(polygon.x1, tile_size);
|
|
123
|
+
const std::int64_t ty1 = device_floor_div(polygon.y1, tile_size);
|
|
124
|
+
std::uint64_t out = offsets[index];
|
|
125
|
+
for (std::int64_t tx = tx0; tx <= tx1; ++tx) {
|
|
126
|
+
for (std::int64_t ty = ty0; ty <= ty1; ++ty) {
|
|
127
|
+
assignments[out++] = DeviceTileAssignment{
|
|
128
|
+
polygon.layer,
|
|
129
|
+
polygon.datatype,
|
|
130
|
+
tx,
|
|
131
|
+
ty,
|
|
132
|
+
polygon.canonical_id,
|
|
133
|
+
side,
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
void check_cuda(cudaError_t status, const char* action) {
|
|
140
|
+
if (status != cudaSuccess) {
|
|
141
|
+
throw std::runtime_error(std::string(action) + ": " + cudaGetErrorString(status));
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
template <class T>
|
|
146
|
+
class DeviceBuffer {
|
|
147
|
+
public:
|
|
148
|
+
explicit DeviceBuffer(std::size_t count) : count_(count) {
|
|
149
|
+
if (count_) {
|
|
150
|
+
check_cuda(cudaMalloc(&ptr_, sizeof(T) * count_), "cudaMalloc");
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
~DeviceBuffer() {
|
|
155
|
+
if (ptr_) {
|
|
156
|
+
(void)cudaFree(ptr_);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
DeviceBuffer(const DeviceBuffer&) = delete;
|
|
161
|
+
DeviceBuffer& operator=(const DeviceBuffer&) = delete;
|
|
162
|
+
|
|
163
|
+
T* get() { return ptr_; }
|
|
164
|
+
const T* get() const { return ptr_; }
|
|
165
|
+
|
|
166
|
+
private:
|
|
167
|
+
T* ptr_ = nullptr;
|
|
168
|
+
std::size_t count_ = 0;
|
|
169
|
+
};
|
|
170
|
+
|
|
171
|
+
std::vector<DevicePolygon> pack_polygons(const std::vector<PolygonRecord>& polygons, const std::vector<std::uint64_t>& ids) {
|
|
172
|
+
std::vector<DevicePolygon> out;
|
|
173
|
+
out.reserve(polygons.size());
|
|
174
|
+
for (std::size_t i = 0; i < polygons.size(); ++i) {
|
|
175
|
+
out.push_back(DevicePolygon{
|
|
176
|
+
polygons[i].bbox.x0,
|
|
177
|
+
polygons[i].bbox.y0,
|
|
178
|
+
polygons[i].bbox.x1,
|
|
179
|
+
polygons[i].bbox.y1,
|
|
180
|
+
polygons[i].layer.layer,
|
|
181
|
+
polygons[i].layer.datatype,
|
|
182
|
+
ids[i],
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
return out;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
} // namespace
|
|
189
|
+
|
|
190
|
+
CudaAssignmentResult run_cuda_assignment(
|
|
191
|
+
const std::vector<PolygonRecord>& polygons,
|
|
192
|
+
const std::vector<std::uint64_t>& canonical_ids,
|
|
193
|
+
std::uint8_t side,
|
|
194
|
+
const TileConfig& config) {
|
|
195
|
+
if (config.tile_size <= 0 || config.max_tiles_per_polygon == 0) {
|
|
196
|
+
throw std::invalid_argument("invalid tile config");
|
|
197
|
+
}
|
|
198
|
+
if (polygons.size() != canonical_ids.size()) {
|
|
199
|
+
throw std::invalid_argument("canonical id vector length must match polygon count");
|
|
200
|
+
}
|
|
201
|
+
if (polygons.empty()) {
|
|
202
|
+
return {};
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
const std::uint64_t n = polygons.size();
|
|
206
|
+
const auto packed = pack_polygons(polygons, canonical_ids);
|
|
207
|
+
DeviceBuffer<DevicePolygon> d_polygons(n);
|
|
208
|
+
DeviceBuffer<std::uint64_t> d_counts(n);
|
|
209
|
+
DeviceBuffer<std::uint8_t> d_oversized_flags(n);
|
|
210
|
+
DeviceBuffer<DeviceOversized> d_oversized(n);
|
|
211
|
+
DeviceBuffer<std::uint32_t> d_errors(n);
|
|
212
|
+
check_cuda(cudaMemcpy(d_polygons.get(), packed.data(), sizeof(DevicePolygon) * packed.size(), cudaMemcpyHostToDevice), "copy polygons");
|
|
213
|
+
check_cuda(cudaMemset(d_errors.get(), 0, sizeof(std::uint32_t) * n), "clear errors");
|
|
214
|
+
|
|
215
|
+
const int threads = 256;
|
|
216
|
+
const int blocks = static_cast<int>((n + threads - 1) / threads);
|
|
217
|
+
count_kernel<<<blocks, threads>>>(
|
|
218
|
+
d_polygons.get(),
|
|
219
|
+
n,
|
|
220
|
+
config.tile_size,
|
|
221
|
+
config.max_tiles_per_polygon,
|
|
222
|
+
d_counts.get(),
|
|
223
|
+
d_oversized_flags.get(),
|
|
224
|
+
d_oversized.get(),
|
|
225
|
+
side,
|
|
226
|
+
d_errors.get());
|
|
227
|
+
check_cuda(cudaGetLastError(), "launch count kernel");
|
|
228
|
+
check_cuda(cudaDeviceSynchronize(), "run count kernel");
|
|
229
|
+
|
|
230
|
+
std::vector<std::uint32_t> errors(n);
|
|
231
|
+
check_cuda(cudaMemcpy(errors.data(), d_errors.get(), sizeof(std::uint32_t) * n, cudaMemcpyDeviceToHost), "copy errors");
|
|
232
|
+
for (std::uint32_t error : errors) {
|
|
233
|
+
if (error) {
|
|
234
|
+
throw std::overflow_error("CUDA assignment count overflow or invalid span");
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
std::vector<std::uint64_t> counts(n);
|
|
239
|
+
std::vector<std::uint8_t> oversized_flags(n);
|
|
240
|
+
std::vector<DeviceOversized> oversized_raw(n);
|
|
241
|
+
check_cuda(cudaMemcpy(counts.data(), d_counts.get(), sizeof(std::uint64_t) * n, cudaMemcpyDeviceToHost), "copy counts");
|
|
242
|
+
check_cuda(cudaMemcpy(oversized_flags.data(), d_oversized_flags.get(), sizeof(std::uint8_t) * n, cudaMemcpyDeviceToHost), "copy oversized flags");
|
|
243
|
+
check_cuda(cudaMemcpy(oversized_raw.data(), d_oversized.get(), sizeof(DeviceOversized) * n, cudaMemcpyDeviceToHost), "copy oversized");
|
|
244
|
+
|
|
245
|
+
std::vector<std::uint64_t> offsets(n);
|
|
246
|
+
std::uint64_t total = 0;
|
|
247
|
+
for (std::size_t i = 0; i < counts.size(); ++i) {
|
|
248
|
+
offsets[i] = total;
|
|
249
|
+
if (counts[i] > UINT64_MAX - total) {
|
|
250
|
+
throw std::overflow_error("total assignment count overflow");
|
|
251
|
+
}
|
|
252
|
+
total += counts[i];
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
CudaAssignmentResult result;
|
|
256
|
+
result.total_assignment_count = total;
|
|
257
|
+
for (std::size_t i = 0; i < oversized_flags.size(); ++i) {
|
|
258
|
+
if (oversized_flags[i]) {
|
|
259
|
+
const auto item = oversized_raw[i];
|
|
260
|
+
result.oversized.push_back(
|
|
261
|
+
OversizedPolygon{
|
|
262
|
+
LayerSpec{item.layer, item.datatype},
|
|
263
|
+
item.canonical_id,
|
|
264
|
+
item.side,
|
|
265
|
+
BBox{item.tx0, item.ty0, item.tx1, item.ty1},
|
|
266
|
+
});
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
if (total == 0) {
|
|
271
|
+
return result;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
DeviceBuffer<std::uint64_t> d_offsets(n);
|
|
275
|
+
DeviceBuffer<DeviceTileAssignment> d_assignments(total);
|
|
276
|
+
check_cuda(cudaMemcpy(d_offsets.get(), offsets.data(), sizeof(std::uint64_t) * n, cudaMemcpyHostToDevice), "copy offsets");
|
|
277
|
+
emit_kernel<<<blocks, threads>>>(
|
|
278
|
+
d_polygons.get(),
|
|
279
|
+
n,
|
|
280
|
+
config.tile_size,
|
|
281
|
+
d_offsets.get(),
|
|
282
|
+
d_oversized_flags.get(),
|
|
283
|
+
d_assignments.get(),
|
|
284
|
+
side);
|
|
285
|
+
check_cuda(cudaGetLastError(), "launch emit kernel");
|
|
286
|
+
check_cuda(cudaDeviceSynchronize(), "run emit kernel");
|
|
287
|
+
|
|
288
|
+
std::vector<DeviceTileAssignment> raw_assignments(total);
|
|
289
|
+
check_cuda(cudaMemcpy(raw_assignments.data(), d_assignments.get(), sizeof(DeviceTileAssignment) * total, cudaMemcpyDeviceToHost), "copy assignments");
|
|
290
|
+
result.assignments.reserve(raw_assignments.size());
|
|
291
|
+
for (const auto item : raw_assignments) {
|
|
292
|
+
result.assignments.push_back(
|
|
293
|
+
TileAssignment{
|
|
294
|
+
LayerSpec{item.layer, item.datatype},
|
|
295
|
+
item.tx,
|
|
296
|
+
item.ty,
|
|
297
|
+
item.canonical_id,
|
|
298
|
+
item.side,
|
|
299
|
+
});
|
|
300
|
+
}
|
|
301
|
+
return result;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
} // namespace gdsdiff
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
#include "core.hpp"
|
|
4
|
+
|
|
5
|
+
#include <cstdint>
|
|
6
|
+
#include <vector>
|
|
7
|
+
|
|
8
|
+
namespace gdsdiff {
|
|
9
|
+
|
|
10
|
+
struct TileAssignment {
|
|
11
|
+
LayerSpec layer;
|
|
12
|
+
std::int64_t tx = 0;
|
|
13
|
+
std::int64_t ty = 0;
|
|
14
|
+
std::uint64_t canonical_id = 0;
|
|
15
|
+
std::uint8_t side = 0;
|
|
16
|
+
|
|
17
|
+
friend bool operator==(const TileAssignment&, const TileAssignment&) = default;
|
|
18
|
+
friend auto operator<=>(const TileAssignment&, const TileAssignment&) = default;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
struct CudaAssignmentResult {
|
|
22
|
+
std::vector<TileAssignment> assignments;
|
|
23
|
+
std::vector<OversizedPolygon> oversized;
|
|
24
|
+
std::uint64_t total_assignment_count = 0;
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
CudaAssignmentResult run_cuda_assignment(
|
|
28
|
+
const std::vector<PolygonRecord>& polygons,
|
|
29
|
+
const std::vector<std::uint64_t>& canonical_ids,
|
|
30
|
+
std::uint8_t side,
|
|
31
|
+
const TileConfig& config);
|
|
32
|
+
|
|
33
|
+
} // namespace gdsdiff
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
#include "cuda_candidates.cuh"
|
|
2
|
+
|
|
3
|
+
#include "cuda_assignment.cuh"
|
|
4
|
+
|
|
5
|
+
#include <thrust/device_vector.h>
|
|
6
|
+
#include <thrust/equal.h>
|
|
7
|
+
#include <thrust/host_vector.h>
|
|
8
|
+
#include <thrust/reduce.h>
|
|
9
|
+
#include <thrust/sort.h>
|
|
10
|
+
#include <thrust/tuple.h>
|
|
11
|
+
#include <thrust/unique.h>
|
|
12
|
+
|
|
13
|
+
#include <algorithm>
|
|
14
|
+
#include <map>
|
|
15
|
+
#include <set>
|
|
16
|
+
#include <stdexcept>
|
|
17
|
+
|
|
18
|
+
namespace gdsdiff {
|
|
19
|
+
namespace {
|
|
20
|
+
|
|
21
|
+
struct AssignmentKey {
|
|
22
|
+
std::int32_t layer;
|
|
23
|
+
std::int32_t datatype;
|
|
24
|
+
std::int64_t tx;
|
|
25
|
+
std::int64_t ty;
|
|
26
|
+
std::uint64_t canonical_id;
|
|
27
|
+
|
|
28
|
+
__host__ __device__ bool operator<(const AssignmentKey& other) const {
|
|
29
|
+
return thrust::make_tuple(layer, datatype, tx, ty, canonical_id)
|
|
30
|
+
< thrust::make_tuple(other.layer, other.datatype, other.tx, other.ty, other.canonical_id);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
__host__ __device__ bool operator==(const AssignmentKey& other) const {
|
|
34
|
+
return layer == other.layer && datatype == other.datatype && tx == other.tx && ty == other.ty
|
|
35
|
+
&& canonical_id == other.canonical_id;
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
struct AssignmentKeyEqual {
|
|
40
|
+
__host__ __device__ bool operator()(const AssignmentKey& left, const AssignmentKey& right) const {
|
|
41
|
+
return left == right;
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
struct SideOr {
|
|
46
|
+
__host__ __device__ std::uint8_t operator()(std::uint8_t left, std::uint8_t right) const {
|
|
47
|
+
return static_cast<std::uint8_t>(left | right);
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
void merge_oversized(
|
|
52
|
+
std::map<std::tuple<LayerSpec, std::uint64_t, BBox>, std::uint8_t>& out,
|
|
53
|
+
const std::vector<OversizedPolygon>& items) {
|
|
54
|
+
for (const auto& item : items) {
|
|
55
|
+
auto key = std::make_tuple(item.layer, item.canonical_id, item.tile_bbox);
|
|
56
|
+
out[key] = static_cast<std::uint8_t>(out[key] | item.side_mask);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
} // namespace
|
|
61
|
+
|
|
62
|
+
TilePrefilterResult run_cuda_candidate_compaction(
|
|
63
|
+
const std::vector<PolygonRecord>& old_polygons,
|
|
64
|
+
const std::vector<std::uint64_t>& old_canonical_ids,
|
|
65
|
+
const std::vector<PolygonRecord>& new_polygons,
|
|
66
|
+
const std::vector<std::uint64_t>& new_canonical_ids,
|
|
67
|
+
const TileConfig& config) {
|
|
68
|
+
const auto old_assignments = run_cuda_assignment(old_polygons, old_canonical_ids, kOldSide, config);
|
|
69
|
+
const auto new_assignments = run_cuda_assignment(new_polygons, new_canonical_ids, kNewSide, config);
|
|
70
|
+
|
|
71
|
+
std::vector<AssignmentKey> host_keys;
|
|
72
|
+
std::vector<std::uint8_t> host_sides;
|
|
73
|
+
host_keys.reserve(old_assignments.assignments.size() + new_assignments.assignments.size());
|
|
74
|
+
host_sides.reserve(host_keys.capacity());
|
|
75
|
+
for (const auto& item : old_assignments.assignments) {
|
|
76
|
+
host_keys.push_back(AssignmentKey{item.layer.layer, item.layer.datatype, item.tx, item.ty, item.canonical_id});
|
|
77
|
+
host_sides.push_back(item.side);
|
|
78
|
+
}
|
|
79
|
+
for (const auto& item : new_assignments.assignments) {
|
|
80
|
+
host_keys.push_back(AssignmentKey{item.layer.layer, item.layer.datatype, item.tx, item.ty, item.canonical_id});
|
|
81
|
+
host_sides.push_back(item.side);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
TilePrefilterResult result;
|
|
85
|
+
result.assignment_count = host_keys.size();
|
|
86
|
+
|
|
87
|
+
if (!host_keys.empty()) {
|
|
88
|
+
thrust::device_vector<AssignmentKey> keys = host_keys;
|
|
89
|
+
thrust::device_vector<std::uint8_t> sides = host_sides;
|
|
90
|
+
thrust::sort_by_key(keys.begin(), keys.end(), sides.begin());
|
|
91
|
+
|
|
92
|
+
thrust::device_vector<AssignmentKey> reduced_keys(keys.size());
|
|
93
|
+
thrust::device_vector<std::uint8_t> reduced_sides(sides.size());
|
|
94
|
+
auto ends = thrust::reduce_by_key(
|
|
95
|
+
keys.begin(),
|
|
96
|
+
keys.end(),
|
|
97
|
+
sides.begin(),
|
|
98
|
+
reduced_keys.begin(),
|
|
99
|
+
reduced_sides.begin(),
|
|
100
|
+
AssignmentKeyEqual{},
|
|
101
|
+
SideOr{});
|
|
102
|
+
const auto reduced_count = static_cast<std::size_t>(ends.first - reduced_keys.begin());
|
|
103
|
+
result.collapsed_pair_count = reduced_count;
|
|
104
|
+
reduced_keys.resize(reduced_count);
|
|
105
|
+
reduced_sides.resize(reduced_count);
|
|
106
|
+
|
|
107
|
+
thrust::host_vector<AssignmentKey> final_keys = reduced_keys;
|
|
108
|
+
thrust::host_vector<std::uint8_t> final_sides = reduced_sides;
|
|
109
|
+
std::set<TileKey> candidate_tiles;
|
|
110
|
+
for (std::size_t i = 0; i < reduced_count; ++i) {
|
|
111
|
+
if (final_sides[i] != kBothSides) {
|
|
112
|
+
candidate_tiles.insert(TileKey{LayerSpec{final_keys[i].layer, final_keys[i].datatype}, final_keys[i].tx, final_keys[i].ty});
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
result.candidate_tiles.assign(candidate_tiles.begin(), candidate_tiles.end());
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
std::map<std::tuple<LayerSpec, std::uint64_t, BBox>, std::uint8_t> oversized_side_masks;
|
|
119
|
+
merge_oversized(oversized_side_masks, old_assignments.oversized);
|
|
120
|
+
merge_oversized(oversized_side_masks, new_assignments.oversized);
|
|
121
|
+
for (const auto& [key, side_mask] : oversized_side_masks) {
|
|
122
|
+
if (side_mask == kBothSides) {
|
|
123
|
+
++result.oversized_matched_count;
|
|
124
|
+
} else {
|
|
125
|
+
result.oversized_candidates.push_back(
|
|
126
|
+
OversizedPolygon{std::get<0>(key), std::get<1>(key), side_mask, std::get<2>(key)});
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
std::sort(result.oversized_candidates.begin(), result.oversized_candidates.end());
|
|
130
|
+
return result;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
} // namespace gdsdiff
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
#include "core.hpp"
|
|
4
|
+
|
|
5
|
+
#include <cstdint>
|
|
6
|
+
#include <vector>
|
|
7
|
+
|
|
8
|
+
namespace gdsdiff {
|
|
9
|
+
|
|
10
|
+
TilePrefilterResult run_cuda_candidate_compaction(
|
|
11
|
+
const std::vector<PolygonRecord>& old_polygons,
|
|
12
|
+
const std::vector<std::uint64_t>& old_canonical_ids,
|
|
13
|
+
const std::vector<PolygonRecord>& new_polygons,
|
|
14
|
+
const std::vector<std::uint64_t>& new_canonical_ids,
|
|
15
|
+
const TileConfig& config);
|
|
16
|
+
|
|
17
|
+
} // namespace gdsdiff
|