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.
Files changed (85) hide show
  1. gdsdiff/__init__.py +148 -0
  2. gdsdiff/__main__.py +7 -0
  3. gdsdiff/_environment.py +12 -0
  4. gdsdiff/_version.py +34 -0
  5. gdsdiff/acceptance_evidence.py +530 -0
  6. gdsdiff/api.py +2955 -0
  7. gdsdiff/backends/__init__.py +26 -0
  8. gdsdiff/backends/base.py +77 -0
  9. gdsdiff/backends/boundary_loop_ctypes.py +191 -0
  10. gdsdiff/backends/cpu.py +20 -0
  11. gdsdiff/backends/cpu_ctypes.py +255 -0
  12. gdsdiff/backends/cuda.py +40 -0
  13. gdsdiff/backends/cuda_ctypes.py +1297 -0
  14. gdsdiff/backends/exact.py +424 -0
  15. gdsdiff/backends/geometry_ctypes.py +252 -0
  16. gdsdiff/backends/identity.py +53 -0
  17. gdsdiff/backends/metal.py +198 -0
  18. gdsdiff/backends/metal_ctypes.py +866 -0
  19. gdsdiff/backends/polygon_extract_ctypes.py +233 -0
  20. gdsdiff/backends/structural_ctypes.py +130 -0
  21. gdsdiff/boundary_loops.py +616 -0
  22. gdsdiff/cache.py +544 -0
  23. gdsdiff/canonicalize.py +176 -0
  24. gdsdiff/cli.py +352 -0
  25. gdsdiff/config.py +257 -0
  26. gdsdiff/cuda_setup.py +174 -0
  27. gdsdiff/design_history.py +65 -0
  28. gdsdiff/diagnostics.py +42 -0
  29. gdsdiff/diff_gds.py +66 -0
  30. gdsdiff/exact_diff_markdown.py +979 -0
  31. gdsdiff/exact_oracle.py +649 -0
  32. gdsdiff/extract.py +376 -0
  33. gdsdiff/gds_metadata.py +43 -0
  34. gdsdiff/geometry.py +112 -0
  35. gdsdiff/grid.py +143 -0
  36. gdsdiff/hierarchy.py +215 -0
  37. gdsdiff/hierarchy_extract.py +263 -0
  38. gdsdiff/inventory.py +153 -0
  39. gdsdiff/multiprocessing_support.py +39 -0
  40. gdsdiff/native.py +135 -0
  41. gdsdiff/native_cache.py +265 -0
  42. gdsdiff/native_src/cpu_prefilter.cpp +349 -0
  43. gdsdiff/native_src/gds_boundary_loops.cpp +505 -0
  44. gdsdiff/native_src/gds_geometry_scan.cpp +601 -0
  45. gdsdiff/native_src/gds_polygon_extract.cpp +594 -0
  46. gdsdiff/native_src/gds_structural_scan.cpp +335 -0
  47. gdsdiff/native_src/gdsdiff/CMakeLists.txt +74 -0
  48. gdsdiff/native_src/gdsdiff/core.cpp +191 -0
  49. gdsdiff/native_src/gdsdiff/core.hpp +96 -0
  50. gdsdiff/native_src/gdsdiff/cuda_assignment.cu +304 -0
  51. gdsdiff/native_src/gdsdiff/cuda_assignment.cuh +33 -0
  52. gdsdiff/native_src/gdsdiff/cuda_candidates.cu +133 -0
  53. gdsdiff/native_src/gdsdiff/cuda_candidates.cuh +17 -0
  54. gdsdiff/native_src/gdsdiff/cuda_ctypes.cu +4528 -0
  55. gdsdiff/native_src/gdsdiff/cuda_memory.cu +194 -0
  56. gdsdiff/native_src/gdsdiff/cuda_memory.cuh +34 -0
  57. gdsdiff/native_src/gdsdiff/metal_ctypes.mm +2791 -0
  58. gdsdiff/native_src/gdsdiff/python_bindings.cpp +21 -0
  59. gdsdiff/native_src/gdsdiff/test_core.cpp +93 -0
  60. gdsdiff/native_src/gdsdiff/test_cuda_assignment.cu +109 -0
  61. gdsdiff/native_src/gdsdiff/test_cuda_candidates.cu +94 -0
  62. gdsdiff/native_src/gdsdiff/test_cuda_memory.cu +97 -0
  63. gdsdiff/policy.py +305 -0
  64. gdsdiff/polygon_components.py +860 -0
  65. gdsdiff/profiles.py +152 -0
  66. gdsdiff/py.typed +1 -0
  67. gdsdiff/rect_coverage.py +384 -0
  68. gdsdiff/report.py +354 -0
  69. gdsdiff/schemas/gdsdiff_report-v1.schema.json +92 -0
  70. gdsdiff/semantics.py +75 -0
  71. gdsdiff/source_edge_diff.py +1120 -0
  72. gdsdiff/spatial.py +71 -0
  73. gdsdiff/structural_guard.py +161 -0
  74. gdsdiff/surplus_coverage.py +255 -0
  75. gdsdiff/synthetic_suite.py +1643 -0
  76. gdsdiff/templates.py +709 -0
  77. gdsdiff/tiling.py +153 -0
  78. gdsdiff/windowed_exact.py +270 -0
  79. gdsdiff/windows.py +184 -0
  80. gdsdiff-0.1.0.dist-info/METADATA +135 -0
  81. gdsdiff-0.1.0.dist-info/RECORD +85 -0
  82. gdsdiff-0.1.0.dist-info/WHEEL +5 -0
  83. gdsdiff-0.1.0.dist-info/entry_points.txt +4 -0
  84. gdsdiff-0.1.0.dist-info/licenses/LICENSE +674 -0
  85. gdsdiff-0.1.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,349 @@
1
+ #include <algorithm>
2
+ #include <cstdint>
3
+ #include <cstdlib>
4
+ #include <exception>
5
+ #include <limits>
6
+ #include <stdexcept>
7
+ #include <string>
8
+ #include <tuple>
9
+ #include <vector>
10
+
11
+ #ifdef _OPENMP
12
+ #include <omp.h>
13
+ #endif
14
+
15
+ namespace {
16
+
17
+ constexpr std::uint8_t kOldSide = 1;
18
+ constexpr std::uint8_t kNewSide = 2;
19
+ constexpr std::uint8_t kBothSides = kOldSide | kNewSide;
20
+
21
+ thread_local std::string g_last_error;
22
+
23
+ struct Polygon {
24
+ std::int64_t x0;
25
+ std::int64_t y0;
26
+ std::int64_t x1;
27
+ std::int64_t y1;
28
+ std::int32_t layer;
29
+ std::int32_t datatype;
30
+ std::uint64_t canonical_id;
31
+ };
32
+
33
+ struct AssignmentKey {
34
+ std::int32_t layer;
35
+ std::int32_t datatype;
36
+ std::int64_t tx;
37
+ std::int64_t ty;
38
+ std::uint64_t canonical_id;
39
+
40
+ bool operator<(const AssignmentKey& other) const {
41
+ return std::tie(layer, datatype, tx, ty, canonical_id)
42
+ < std::tie(other.layer, other.datatype, other.tx, other.ty, other.canonical_id);
43
+ }
44
+
45
+ bool operator==(const AssignmentKey& other) const {
46
+ return layer == other.layer && datatype == other.datatype && tx == other.tx && ty == other.ty
47
+ && canonical_id == other.canonical_id;
48
+ }
49
+ };
50
+
51
+ struct Assignment {
52
+ AssignmentKey key;
53
+ std::uint8_t side;
54
+ };
55
+
56
+ struct CandidateTile {
57
+ std::int32_t layer;
58
+ std::int32_t datatype;
59
+ std::int64_t tx;
60
+ std::int64_t ty;
61
+
62
+ bool operator<(const CandidateTile& other) const {
63
+ return std::tie(layer, datatype, tx, ty) < std::tie(other.layer, other.datatype, other.tx, other.ty);
64
+ }
65
+
66
+ bool operator==(const CandidateTile& other) const {
67
+ return layer == other.layer && datatype == other.datatype && tx == other.tx && ty == other.ty;
68
+ }
69
+ };
70
+
71
+ struct OversizedKey {
72
+ std::int32_t layer;
73
+ std::int32_t datatype;
74
+ std::uint64_t canonical_id;
75
+ std::int64_t tx0;
76
+ std::int64_t ty0;
77
+ std::int64_t tx1;
78
+ std::int64_t ty1;
79
+
80
+ bool operator<(const OversizedKey& other) const {
81
+ return std::tie(layer, datatype, canonical_id, tx0, ty0, tx1, ty1)
82
+ < std::tie(other.layer, other.datatype, other.canonical_id, other.tx0, other.ty0, other.tx1, other.ty1);
83
+ }
84
+ };
85
+
86
+ struct OversizedItem {
87
+ OversizedKey key;
88
+ std::uint8_t side;
89
+ };
90
+
91
+ std::int64_t floor_div(std::int64_t value, std::int64_t divisor) {
92
+ std::int64_t quotient = value / divisor;
93
+ const std::int64_t remainder = value % divisor;
94
+ if (remainder != 0 && ((remainder < 0) != (divisor < 0))) {
95
+ --quotient;
96
+ }
97
+ return quotient;
98
+ }
99
+
100
+ std::vector<Polygon> pack_polygons(
101
+ const std::int64_t* bboxes,
102
+ const std::int32_t* layers,
103
+ const std::int32_t* datatypes,
104
+ const std::uint64_t* canonical_ids,
105
+ std::uint64_t count) {
106
+ std::vector<Polygon> out;
107
+ out.reserve(count);
108
+ for (std::uint64_t i = 0; i < count; ++i) {
109
+ out.push_back(Polygon{
110
+ bboxes[i * 4 + 0],
111
+ bboxes[i * 4 + 1],
112
+ bboxes[i * 4 + 2],
113
+ bboxes[i * 4 + 3],
114
+ layers[i],
115
+ datatypes[i],
116
+ canonical_ids[i],
117
+ });
118
+ }
119
+ return out;
120
+ }
121
+
122
+ void append_side(
123
+ std::vector<Assignment>& assignments,
124
+ std::vector<OversizedItem>& oversized_items,
125
+ const std::vector<Polygon>& polygons,
126
+ std::int64_t tile_size,
127
+ std::uint64_t max_tiles_per_polygon,
128
+ std::uint8_t side) {
129
+ if (polygons.empty()) {
130
+ return;
131
+ }
132
+ const int thread_count =
133
+ #ifdef _OPENMP
134
+ std::max(1, omp_get_max_threads());
135
+ #else
136
+ 1;
137
+ #endif
138
+ std::vector<std::vector<Assignment>> local_assignments(thread_count);
139
+ std::vector<std::vector<OversizedItem>> local_oversized(thread_count);
140
+
141
+ #pragma omp parallel
142
+ {
143
+ #ifdef _OPENMP
144
+ const int tid = omp_get_thread_num();
145
+ #else
146
+ const int tid = 0;
147
+ #endif
148
+ auto& local_a = local_assignments[tid];
149
+ auto& local_o = local_oversized[tid];
150
+ #pragma omp for schedule(dynamic, 64)
151
+ for (std::int64_t index = 0; index < static_cast<std::int64_t>(polygons.size()); ++index) {
152
+ const Polygon& polygon = polygons[static_cast<std::size_t>(index)];
153
+ const std::int64_t tx0 = floor_div(polygon.x0, tile_size);
154
+ const std::int64_t ty0 = floor_div(polygon.y0, tile_size);
155
+ const std::int64_t tx1 = floor_div(polygon.x1, tile_size);
156
+ const std::int64_t ty1 = floor_div(polygon.y1, tile_size);
157
+ const std::int64_t span_x = tx1 - tx0 + 1;
158
+ const std::int64_t span_y = ty1 - ty0 + 1;
159
+ if (span_x <= 0 || span_y <= 0) {
160
+ continue;
161
+ }
162
+ const auto span_x_u = static_cast<std::uint64_t>(span_x);
163
+ const auto span_y_u = static_cast<std::uint64_t>(span_y);
164
+ if (span_y_u != 0 && span_x_u > UINT64_MAX / span_y_u) {
165
+ continue;
166
+ }
167
+ const std::uint64_t count = span_x_u * span_y_u;
168
+ if (count > max_tiles_per_polygon) {
169
+ local_o.push_back(OversizedItem{
170
+ OversizedKey{polygon.layer, polygon.datatype, polygon.canonical_id, tx0, ty0, tx1, ty1},
171
+ side,
172
+ });
173
+ continue;
174
+ }
175
+ local_a.reserve(local_a.size() + static_cast<std::size_t>(count));
176
+ for (std::int64_t tx = tx0; tx <= tx1; ++tx) {
177
+ for (std::int64_t ty = ty0; ty <= ty1; ++ty) {
178
+ local_a.push_back(Assignment{
179
+ AssignmentKey{polygon.layer, polygon.datatype, tx, ty, polygon.canonical_id},
180
+ side,
181
+ });
182
+ }
183
+ }
184
+ }
185
+ }
186
+
187
+ std::size_t assignment_total = assignments.size();
188
+ std::size_t oversized_total = oversized_items.size();
189
+ for (const auto& chunk : local_assignments) {
190
+ assignment_total += chunk.size();
191
+ }
192
+ for (const auto& chunk : local_oversized) {
193
+ oversized_total += chunk.size();
194
+ }
195
+ assignments.reserve(assignment_total);
196
+ oversized_items.reserve(oversized_total);
197
+ for (auto& chunk : local_assignments) {
198
+ assignments.insert(assignments.end(), chunk.begin(), chunk.end());
199
+ }
200
+ for (auto& chunk : local_oversized) {
201
+ oversized_items.insert(oversized_items.end(), chunk.begin(), chunk.end());
202
+ }
203
+ }
204
+
205
+ } // namespace
206
+
207
+ extern "C" {
208
+
209
+ struct GdsCpuPrefilterResult {
210
+ std::int64_t* candidate_tiles;
211
+ std::uint64_t candidate_count;
212
+ std::int64_t* oversized;
213
+ std::uint64_t oversized_count;
214
+ std::uint64_t assignment_count;
215
+ std::uint64_t collapsed_pair_count;
216
+ std::uint64_t oversized_matched_count;
217
+ };
218
+
219
+ const char* gds_cpu_last_error() {
220
+ return g_last_error.c_str();
221
+ }
222
+
223
+ void gds_cpu_free_result(GdsCpuPrefilterResult* result) {
224
+ if (!result) {
225
+ return;
226
+ }
227
+ std::free(result->candidate_tiles);
228
+ std::free(result->oversized);
229
+ result->candidate_tiles = nullptr;
230
+ result->oversized = nullptr;
231
+ result->candidate_count = 0;
232
+ result->oversized_count = 0;
233
+ }
234
+
235
+ int gds_cpu_prefilter(
236
+ const std::int64_t* old_bboxes,
237
+ const std::int32_t* old_layers,
238
+ const std::int32_t* old_datatypes,
239
+ const std::uint64_t* old_ids,
240
+ std::uint64_t old_count,
241
+ const std::int64_t* new_bboxes,
242
+ const std::int32_t* new_layers,
243
+ const std::int32_t* new_datatypes,
244
+ const std::uint64_t* new_ids,
245
+ std::uint64_t new_count,
246
+ std::int64_t tile_size,
247
+ std::uint64_t max_tiles_per_polygon,
248
+ GdsCpuPrefilterResult* result) {
249
+ try {
250
+ if (!result) {
251
+ throw std::invalid_argument("result pointer is null");
252
+ }
253
+ *result = GdsCpuPrefilterResult{};
254
+ if (tile_size <= 0 || max_tiles_per_polygon == 0) {
255
+ throw std::invalid_argument("invalid tile config");
256
+ }
257
+ std::vector<Assignment> assignments;
258
+ std::vector<OversizedItem> oversized_items;
259
+ const auto old_polygons = pack_polygons(old_bboxes, old_layers, old_datatypes, old_ids, old_count);
260
+ const auto new_polygons = pack_polygons(new_bboxes, new_layers, new_datatypes, new_ids, new_count);
261
+ append_side(assignments, oversized_items, old_polygons, tile_size, max_tiles_per_polygon, kOldSide);
262
+ append_side(assignments, oversized_items, new_polygons, tile_size, max_tiles_per_polygon, kNewSide);
263
+ result->assignment_count = assignments.size();
264
+
265
+ std::sort(assignments.begin(), assignments.end(), [](const Assignment& left, const Assignment& right) {
266
+ return left.key < right.key;
267
+ });
268
+ std::vector<CandidateTile> candidate_tiles;
269
+ for (std::size_t i = 0; i < assignments.size();) {
270
+ const AssignmentKey key = assignments[i].key;
271
+ std::uint8_t side = 0;
272
+ do {
273
+ side = static_cast<std::uint8_t>(side | assignments[i].side);
274
+ ++i;
275
+ } while (i < assignments.size() && assignments[i].key == key);
276
+ ++result->collapsed_pair_count;
277
+ if (side != kBothSides) {
278
+ candidate_tiles.push_back(CandidateTile{key.layer, key.datatype, key.tx, key.ty});
279
+ }
280
+ }
281
+ std::sort(candidate_tiles.begin(), candidate_tiles.end());
282
+ candidate_tiles.erase(std::unique(candidate_tiles.begin(), candidate_tiles.end()), candidate_tiles.end());
283
+ result->candidate_count = candidate_tiles.size();
284
+ if (!candidate_tiles.empty()) {
285
+ result->candidate_tiles = static_cast<std::int64_t*>(std::malloc(sizeof(std::int64_t) * candidate_tiles.size() * 4));
286
+ if (!result->candidate_tiles) {
287
+ throw std::bad_alloc();
288
+ }
289
+ for (std::size_t i = 0; i < candidate_tiles.size(); ++i) {
290
+ result->candidate_tiles[i * 4 + 0] = candidate_tiles[i].layer;
291
+ result->candidate_tiles[i * 4 + 1] = candidate_tiles[i].datatype;
292
+ result->candidate_tiles[i * 4 + 2] = candidate_tiles[i].tx;
293
+ result->candidate_tiles[i * 4 + 3] = candidate_tiles[i].ty;
294
+ }
295
+ }
296
+
297
+ std::sort(oversized_items.begin(), oversized_items.end(), [](const OversizedItem& left, const OversizedItem& right) {
298
+ return left.key < right.key;
299
+ });
300
+ std::vector<OversizedItem> oversized_out;
301
+ for (std::size_t i = 0; i < oversized_items.size();) {
302
+ const OversizedKey key = oversized_items[i].key;
303
+ std::uint8_t side = 0;
304
+ do {
305
+ side = static_cast<std::uint8_t>(side | oversized_items[i].side);
306
+ ++i;
307
+ } while (i < oversized_items.size() && !(key < oversized_items[i].key) && !(oversized_items[i].key < key));
308
+ if (side == kBothSides) {
309
+ ++result->oversized_matched_count;
310
+ } else {
311
+ oversized_out.push_back(OversizedItem{key, side});
312
+ }
313
+ }
314
+ result->oversized_count = oversized_out.size();
315
+ if (!oversized_out.empty()) {
316
+ result->oversized = static_cast<std::int64_t*>(std::malloc(sizeof(std::int64_t) * oversized_out.size() * 8));
317
+ if (!result->oversized) {
318
+ throw std::bad_alloc();
319
+ }
320
+ for (std::size_t i = 0; i < oversized_out.size(); ++i) {
321
+ const OversizedItem& item = oversized_out[i];
322
+ result->oversized[i * 8 + 0] = item.key.layer;
323
+ result->oversized[i * 8 + 1] = item.key.datatype;
324
+ result->oversized[i * 8 + 2] = static_cast<std::int64_t>(item.key.canonical_id);
325
+ result->oversized[i * 8 + 3] = item.side;
326
+ result->oversized[i * 8 + 4] = item.key.tx0;
327
+ result->oversized[i * 8 + 5] = item.key.ty0;
328
+ result->oversized[i * 8 + 6] = item.key.tx1;
329
+ result->oversized[i * 8 + 7] = item.key.ty1;
330
+ }
331
+ }
332
+ g_last_error.clear();
333
+ return 0;
334
+ } catch (const std::exception& exc) {
335
+ g_last_error = exc.what();
336
+ if (result) {
337
+ gds_cpu_free_result(result);
338
+ }
339
+ return 1;
340
+ } catch (...) {
341
+ g_last_error = "unknown CPU prefilter error";
342
+ if (result) {
343
+ gds_cpu_free_result(result);
344
+ }
345
+ return 1;
346
+ }
347
+ }
348
+
349
+ } // extern "C"