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,194 @@
1
+ #include "cuda_memory.cuh"
2
+
3
+ #include "cuda_candidates.cuh"
4
+
5
+ #include <algorithm>
6
+ #include <limits>
7
+ #include <map>
8
+ #include <set>
9
+ #include <stdexcept>
10
+
11
+ namespace gdsdiff {
12
+ namespace {
13
+
14
+ struct IndexedPolygon {
15
+ PolygonRecord polygon;
16
+ std::uint64_t canonical_id;
17
+ BBox tile_bbox;
18
+ };
19
+
20
+ std::vector<IndexedPolygon> index_polygons(const std::vector<PolygonRecord>& polygons, const std::vector<std::uint64_t>& ids, const TileConfig& config) {
21
+ if (polygons.size() != ids.size()) {
22
+ throw std::invalid_argument("canonical id vector length must match polygon count");
23
+ }
24
+ std::vector<IndexedPolygon> indexed;
25
+ indexed.reserve(polygons.size());
26
+ for (std::size_t i = 0; i < polygons.size(); ++i) {
27
+ indexed.push_back(IndexedPolygon{polygons[i], ids[i], bbox_tile_range(polygons[i].bbox, config.tile_size)});
28
+ }
29
+ return indexed;
30
+ }
31
+
32
+ std::uint64_t tile_count(const BBox& tile_bbox) {
33
+ const std::int64_t span_x = tile_bbox.x1 - tile_bbox.x0 + 1;
34
+ const std::int64_t span_y = tile_bbox.y1 - tile_bbox.y0 + 1;
35
+ if (span_x <= 0 || span_y <= 0) {
36
+ throw std::overflow_error("invalid tile span");
37
+ }
38
+ const auto span_x_u = static_cast<std::uint64_t>(span_x);
39
+ const auto span_y_u = static_cast<std::uint64_t>(span_y);
40
+ if (span_y_u != 0 && span_x_u > std::numeric_limits<std::uint64_t>::max() / span_y_u) {
41
+ throw std::overflow_error("tile span overflow");
42
+ }
43
+ return span_x_u * span_y_u;
44
+ }
45
+
46
+ std::uint64_t estimate_assignment_bytes(
47
+ const std::vector<IndexedPolygon>& old_indexed,
48
+ const std::vector<IndexedPolygon>& new_indexed,
49
+ const TileConfig& config,
50
+ const CudaMemoryBudget& budget,
51
+ std::int64_t row0,
52
+ std::int64_t row1) {
53
+ std::uint64_t assignments = 0;
54
+ auto add = [&](const std::vector<IndexedPolygon>& polygons) {
55
+ for (const auto& item : polygons) {
56
+ if (item.tile_bbox.y1 < row0 || item.tile_bbox.y0 > row1) {
57
+ continue;
58
+ }
59
+ const std::uint64_t count = tile_count(item.tile_bbox);
60
+ if (count <= config.max_tiles_per_polygon) {
61
+ if (assignments > std::numeric_limits<std::uint64_t>::max() - count) {
62
+ throw std::overflow_error("assignment estimate overflow");
63
+ }
64
+ assignments += count;
65
+ }
66
+ }
67
+ };
68
+ add(old_indexed);
69
+ add(new_indexed);
70
+ if (budget.bytes_per_assignment != 0 && assignments > std::numeric_limits<std::uint64_t>::max() / budget.bytes_per_assignment) {
71
+ throw std::overflow_error("assignment byte estimate overflow");
72
+ }
73
+ return assignments * budget.bytes_per_assignment;
74
+ }
75
+
76
+ void subset_for_rows(
77
+ const std::vector<IndexedPolygon>& indexed,
78
+ std::int64_t row0,
79
+ std::int64_t row1,
80
+ std::vector<PolygonRecord>& polygons,
81
+ std::vector<std::uint64_t>& ids) {
82
+ for (const auto& item : indexed) {
83
+ if (item.tile_bbox.y1 < row0 || item.tile_bbox.y0 > row1) {
84
+ continue;
85
+ }
86
+ polygons.push_back(item.polygon);
87
+ ids.push_back(item.canonical_id);
88
+ }
89
+ }
90
+
91
+ } // namespace
92
+
93
+ CudaChunkedCandidateResult run_cuda_candidate_compaction_chunked(
94
+ const std::vector<PolygonRecord>& old_polygons,
95
+ const std::vector<std::uint64_t>& old_canonical_ids,
96
+ const std::vector<PolygonRecord>& new_polygons,
97
+ const std::vector<std::uint64_t>& new_canonical_ids,
98
+ const TileConfig& config,
99
+ const CudaMemoryBudget& budget) {
100
+ if (budget.bytes_per_assignment == 0 || budget.max_assignment_bytes == 0) {
101
+ throw std::invalid_argument("CUDA memory budget values must be positive");
102
+ }
103
+ const auto old_indexed = index_polygons(old_polygons, old_canonical_ids, config);
104
+ const auto new_indexed = index_polygons(new_polygons, new_canonical_ids, config);
105
+ if (old_indexed.empty() && new_indexed.empty()) {
106
+ CudaChunkedCandidateResult result;
107
+ result.stats.chunk_count = 1;
108
+ return result;
109
+ }
110
+
111
+ std::int64_t min_row = std::numeric_limits<std::int64_t>::max();
112
+ std::int64_t max_row = std::numeric_limits<std::int64_t>::min();
113
+ auto expand = [&](const std::vector<IndexedPolygon>& indexed) {
114
+ for (const auto& item : indexed) {
115
+ min_row = std::min(min_row, item.tile_bbox.y0);
116
+ max_row = std::max(max_row, item.tile_bbox.y1);
117
+ }
118
+ };
119
+ expand(old_indexed);
120
+ expand(new_indexed);
121
+
122
+ const std::uint64_t whole_estimate = estimate_assignment_bytes(old_indexed, new_indexed, config, budget, min_row, max_row);
123
+ if (whole_estimate <= budget.max_assignment_bytes) {
124
+ CudaChunkedCandidateResult result;
125
+ result.prefilter = run_cuda_candidate_compaction(old_polygons, old_canonical_ids, new_polygons, new_canonical_ids, config);
126
+ result.stats.chunk_count = 1;
127
+ result.stats.peak_estimated_assignment_bytes = whole_estimate;
128
+ result.stats.used_chunking = false;
129
+ return result;
130
+ }
131
+
132
+ std::set<TileKey> candidate_tiles;
133
+ std::map<std::tuple<LayerSpec, std::uint64_t, BBox>, std::uint8_t> oversized_side_masks;
134
+ TilePrefilterResult merged;
135
+ CudaChunkStats stats;
136
+ stats.used_chunking = true;
137
+
138
+ std::int64_t row = min_row;
139
+ while (row <= max_row) {
140
+ std::int64_t chunk_end = row;
141
+ std::uint64_t estimate = estimate_assignment_bytes(old_indexed, new_indexed, config, budget, row, chunk_end);
142
+ while (chunk_end < max_row) {
143
+ const std::uint64_t next_estimate = estimate_assignment_bytes(old_indexed, new_indexed, config, budget, row, chunk_end + 1);
144
+ if (next_estimate > budget.max_assignment_bytes && chunk_end >= row) {
145
+ break;
146
+ }
147
+ estimate = next_estimate;
148
+ ++chunk_end;
149
+ if (estimate >= budget.max_assignment_bytes) {
150
+ break;
151
+ }
152
+ }
153
+ stats.peak_estimated_assignment_bytes = std::max(stats.peak_estimated_assignment_bytes, estimate);
154
+ ++stats.chunk_count;
155
+
156
+ std::vector<PolygonRecord> old_subset;
157
+ std::vector<PolygonRecord> new_subset;
158
+ std::vector<std::uint64_t> old_ids;
159
+ std::vector<std::uint64_t> new_ids;
160
+ subset_for_rows(old_indexed, row, chunk_end, old_subset, old_ids);
161
+ subset_for_rows(new_indexed, row, chunk_end, new_subset, new_ids);
162
+ auto chunk = run_cuda_candidate_compaction(old_subset, old_ids, new_subset, new_ids, config);
163
+ merged.assignment_count += chunk.assignment_count;
164
+ merged.collapsed_pair_count += chunk.collapsed_pair_count;
165
+ for (const auto& tile : chunk.candidate_tiles) {
166
+ if (tile.ty >= row && tile.ty <= chunk_end) {
167
+ candidate_tiles.insert(tile);
168
+ }
169
+ }
170
+ for (const auto& item : chunk.oversized_candidates) {
171
+ auto key = std::make_tuple(item.layer, item.canonical_id, item.tile_bbox);
172
+ oversized_side_masks[key] = static_cast<std::uint8_t>(oversized_side_masks[key] | item.side_mask);
173
+ }
174
+ row = chunk_end + 1;
175
+ }
176
+
177
+ merged.candidate_tiles.assign(candidate_tiles.begin(), candidate_tiles.end());
178
+ for (const auto& [key, side_mask] : oversized_side_masks) {
179
+ if (side_mask == kBothSides) {
180
+ ++merged.oversized_matched_count;
181
+ } else {
182
+ merged.oversized_candidates.push_back(
183
+ OversizedPolygon{std::get<0>(key), std::get<1>(key), side_mask, std::get<2>(key)});
184
+ }
185
+ }
186
+ std::sort(merged.oversized_candidates.begin(), merged.oversized_candidates.end());
187
+
188
+ CudaChunkedCandidateResult result;
189
+ result.prefilter = std::move(merged);
190
+ result.stats = stats;
191
+ return result;
192
+ }
193
+
194
+ } // namespace gdsdiff
@@ -0,0 +1,34 @@
1
+ #pragma once
2
+
3
+ #include "core.hpp"
4
+
5
+ #include <cstdint>
6
+ #include <vector>
7
+
8
+ namespace gdsdiff {
9
+
10
+ struct CudaMemoryBudget {
11
+ std::uint64_t max_assignment_bytes = 256ull * 1024ull * 1024ull;
12
+ std::uint64_t bytes_per_assignment = 64;
13
+ };
14
+
15
+ struct CudaChunkStats {
16
+ std::uint64_t chunk_count = 0;
17
+ std::uint64_t peak_estimated_assignment_bytes = 0;
18
+ bool used_chunking = false;
19
+ };
20
+
21
+ struct CudaChunkedCandidateResult {
22
+ TilePrefilterResult prefilter;
23
+ CudaChunkStats stats;
24
+ };
25
+
26
+ CudaChunkedCandidateResult run_cuda_candidate_compaction_chunked(
27
+ const std::vector<PolygonRecord>& old_polygons,
28
+ const std::vector<std::uint64_t>& old_canonical_ids,
29
+ const std::vector<PolygonRecord>& new_polygons,
30
+ const std::vector<std::uint64_t>& new_canonical_ids,
31
+ const TileConfig& config,
32
+ const CudaMemoryBudget& budget);
33
+
34
+ } // namespace gdsdiff