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,1297 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import ctypes
|
|
4
|
+
import os
|
|
5
|
+
import subprocess
|
|
6
|
+
from dataclasses import dataclass
|
|
7
|
+
from functools import lru_cache
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
|
|
10
|
+
import numpy as np
|
|
11
|
+
|
|
12
|
+
from ..config import LayerSpec
|
|
13
|
+
from ..exact_oracle import DiffFragment
|
|
14
|
+
from ..geometry import PolygonBuffer
|
|
15
|
+
from ..native import nvcc_architecture_flags, resolve_nvcc_path
|
|
16
|
+
from ..native_cache import content_addressed_library_path, ensure_cached_native_library, native_cache_dir
|
|
17
|
+
from ..polygon_components import PolygonComponent
|
|
18
|
+
from ..tiling import CpuTilePrefilterResult, OversizedPolygon, TileConfig, TileKey
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class CudaCtypesUnavailable(RuntimeError):
|
|
22
|
+
pass
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class _CResult(ctypes.Structure):
|
|
26
|
+
_fields_ = [
|
|
27
|
+
("candidate_tiles", ctypes.POINTER(ctypes.c_int64)),
|
|
28
|
+
("candidate_count", ctypes.c_uint64),
|
|
29
|
+
("oversized", ctypes.POINTER(ctypes.c_int64)),
|
|
30
|
+
("oversized_count", ctypes.c_uint64),
|
|
31
|
+
("assignment_count", ctypes.c_uint64),
|
|
32
|
+
("collapsed_pair_count", ctypes.c_uint64),
|
|
33
|
+
("oversized_matched_count", ctypes.c_uint64),
|
|
34
|
+
]
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class _CByteCompareResult(ctypes.Structure):
|
|
38
|
+
_fields_ = [
|
|
39
|
+
("old_size", ctypes.c_uint64),
|
|
40
|
+
("new_size", ctypes.c_uint64),
|
|
41
|
+
("same", ctypes.c_uint8),
|
|
42
|
+
]
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
class _CPolygonRecordDeltaResult(ctypes.Structure):
|
|
46
|
+
_fields_ = [
|
|
47
|
+
("record_count", ctypes.c_uint64),
|
|
48
|
+
("layers", ctypes.POINTER(ctypes.c_int32)),
|
|
49
|
+
("datatypes", ctypes.POINTER(ctypes.c_int32)),
|
|
50
|
+
("directions", ctypes.POINTER(ctypes.c_uint8)),
|
|
51
|
+
("counts", ctypes.POINTER(ctypes.c_uint64)),
|
|
52
|
+
("bboxes", ctypes.POINTER(ctypes.c_int64)),
|
|
53
|
+
("vertex_counts", ctypes.POINTER(ctypes.c_uint64)),
|
|
54
|
+
("flags", ctypes.POINTER(ctypes.c_uint32)),
|
|
55
|
+
("hashes", ctypes.POINTER(ctypes.c_uint64)),
|
|
56
|
+
("representative_indices", ctypes.POINTER(ctypes.c_uint64)),
|
|
57
|
+
("old_input_count", ctypes.c_uint64),
|
|
58
|
+
("new_input_count", ctypes.c_uint64),
|
|
59
|
+
("matched_record_count", ctypes.c_uint64),
|
|
60
|
+
("reduced_key_count", ctypes.c_uint64),
|
|
61
|
+
("cuda_kernel_count", ctypes.c_uint64),
|
|
62
|
+
("cuda_event_elapsed_ms", ctypes.c_float),
|
|
63
|
+
]
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
class _CBoundaryLoopResult(ctypes.Structure):
|
|
67
|
+
_fields_ = [
|
|
68
|
+
("fragment_count", ctypes.c_uint64),
|
|
69
|
+
("vertex_count", ctypes.c_uint64),
|
|
70
|
+
("vertices_xy", ctypes.POINTER(ctypes.c_int64)),
|
|
71
|
+
("fragment_offsets", ctypes.POINTER(ctypes.c_uint64)),
|
|
72
|
+
("bboxes", ctypes.POINTER(ctypes.c_int64)),
|
|
73
|
+
("twice_areas", ctypes.POINTER(ctypes.c_int64)),
|
|
74
|
+
("dropped_zero_area_loop_count", ctypes.c_uint64),
|
|
75
|
+
("cuda_kernel_count", ctypes.c_uint64),
|
|
76
|
+
("cuda_event_elapsed_ms", ctypes.c_float),
|
|
77
|
+
]
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
class _CBoundaryEdgesResult(ctypes.Structure):
|
|
81
|
+
_fields_ = [
|
|
82
|
+
("edge_count", ctypes.c_uint64),
|
|
83
|
+
("edges", ctypes.POINTER(ctypes.c_int64)),
|
|
84
|
+
("cuda_kernel_count", ctypes.c_uint64),
|
|
85
|
+
("cuda_event_elapsed_ms", ctypes.c_float),
|
|
86
|
+
]
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
class _CContextOverlapPairsResult(ctypes.Structure):
|
|
90
|
+
_fields_ = [
|
|
91
|
+
("pair_count", ctypes.c_uint64),
|
|
92
|
+
("component_indices", ctypes.POINTER(ctypes.c_uint64)),
|
|
93
|
+
("polygon_indices", ctypes.POINTER(ctypes.c_uint64)),
|
|
94
|
+
("cuda_kernel_count", ctypes.c_uint64),
|
|
95
|
+
("cuda_event_elapsed_ms", ctypes.c_float),
|
|
96
|
+
]
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
@dataclass(frozen=True)
|
|
100
|
+
class _PackedBuffer:
|
|
101
|
+
bboxes: np.ndarray
|
|
102
|
+
layers: np.ndarray
|
|
103
|
+
datatypes: np.ndarray
|
|
104
|
+
canonical_ids: np.ndarray
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
@dataclass(frozen=True)
|
|
108
|
+
class CudaByteCompare:
|
|
109
|
+
old_size: int
|
|
110
|
+
new_size: int
|
|
111
|
+
same: bool
|
|
112
|
+
method: str = "cuda-byte-compare"
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
@dataclass(frozen=True)
|
|
116
|
+
class CudaPolygonRecordDelta:
|
|
117
|
+
layers: np.ndarray
|
|
118
|
+
datatypes: np.ndarray
|
|
119
|
+
directions: np.ndarray
|
|
120
|
+
counts: np.ndarray
|
|
121
|
+
bboxes: np.ndarray
|
|
122
|
+
vertex_counts: np.ndarray
|
|
123
|
+
flags: np.ndarray
|
|
124
|
+
hashes: np.ndarray
|
|
125
|
+
representative_indices: np.ndarray
|
|
126
|
+
old_input_count: int
|
|
127
|
+
new_input_count: int
|
|
128
|
+
matched_record_count: int
|
|
129
|
+
reduced_key_count: int
|
|
130
|
+
cuda_kernel_count: int
|
|
131
|
+
cuda_event_elapsed_s: float
|
|
132
|
+
transferred_bytes: int
|
|
133
|
+
|
|
134
|
+
@property
|
|
135
|
+
def record_count(self) -> int:
|
|
136
|
+
return int(len(self.directions))
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
@dataclass(frozen=True)
|
|
140
|
+
class CudaBoundaryLoopResult:
|
|
141
|
+
fragments: tuple[DiffFragment, ...]
|
|
142
|
+
dropped_zero_area_loop_count: int
|
|
143
|
+
cuda_kernel_count: int
|
|
144
|
+
cuda_event_elapsed_s: float
|
|
145
|
+
transferred_bytes: int
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
@dataclass(frozen=True)
|
|
149
|
+
class CudaBoundaryEdgesResult:
|
|
150
|
+
edges: np.ndarray
|
|
151
|
+
cuda_kernel_count: int
|
|
152
|
+
cuda_event_elapsed_s: float
|
|
153
|
+
transferred_bytes: int
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
@dataclass(frozen=True)
|
|
157
|
+
class CudaContextOverlapPairs:
|
|
158
|
+
component_indices: np.ndarray
|
|
159
|
+
polygon_indices: np.ndarray
|
|
160
|
+
cuda_kernel_count: int
|
|
161
|
+
cuda_event_elapsed_s: float
|
|
162
|
+
transferred_bytes: int
|
|
163
|
+
|
|
164
|
+
@property
|
|
165
|
+
def pair_count(self) -> int:
|
|
166
|
+
return int(len(self.component_indices))
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
def run_cuda_ctypes_tile_prefilter(
|
|
170
|
+
old_buffer: PolygonBuffer,
|
|
171
|
+
old_canonical_ids,
|
|
172
|
+
new_buffer: PolygonBuffer,
|
|
173
|
+
new_canonical_ids,
|
|
174
|
+
config: TileConfig,
|
|
175
|
+
) -> CpuTilePrefilterResult:
|
|
176
|
+
library = _load_library()
|
|
177
|
+
old_packed = _pack_buffer(old_buffer, old_canonical_ids)
|
|
178
|
+
new_packed = _pack_buffer(new_buffer, new_canonical_ids)
|
|
179
|
+
old_packed, new_packed, skipped_matched = _skip_exact_tile_bbox_matches(old_packed, new_packed, config)
|
|
180
|
+
result = _CResult()
|
|
181
|
+
status = library.gds_cuda_prefilter(
|
|
182
|
+
_ptr_i64(old_packed.bboxes),
|
|
183
|
+
_ptr_i32(old_packed.layers),
|
|
184
|
+
_ptr_i32(old_packed.datatypes),
|
|
185
|
+
_ptr_u64(old_packed.canonical_ids),
|
|
186
|
+
ctypes.c_uint64(len(old_packed.canonical_ids)),
|
|
187
|
+
_ptr_i64(new_packed.bboxes),
|
|
188
|
+
_ptr_i32(new_packed.layers),
|
|
189
|
+
_ptr_i32(new_packed.datatypes),
|
|
190
|
+
_ptr_u64(new_packed.canonical_ids),
|
|
191
|
+
ctypes.c_uint64(len(new_packed.canonical_ids)),
|
|
192
|
+
ctypes.c_int64(config.tile_size),
|
|
193
|
+
ctypes.c_uint64(config.max_tiles_per_polygon),
|
|
194
|
+
ctypes.byref(result),
|
|
195
|
+
)
|
|
196
|
+
if status != 0:
|
|
197
|
+
message = library.gds_cuda_last_error()
|
|
198
|
+
raise CudaCtypesUnavailable(message.decode("utf-8") if message else "CUDA prefilter failed")
|
|
199
|
+
try:
|
|
200
|
+
candidate_tiles = _candidate_tiles_from_result(result)
|
|
201
|
+
oversized = _oversized_from_result(result)
|
|
202
|
+
return CpuTilePrefilterResult(
|
|
203
|
+
candidate_tiles=candidate_tiles,
|
|
204
|
+
oversized_candidates=oversized,
|
|
205
|
+
assignment_count=int(result.assignment_count),
|
|
206
|
+
collapsed_pair_count=int(result.collapsed_pair_count),
|
|
207
|
+
oversized_matched_count=int(result.oversized_matched_count) + skipped_matched,
|
|
208
|
+
)
|
|
209
|
+
finally:
|
|
210
|
+
library.gds_cuda_free_result(ctypes.byref(result))
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
def cuda_ctypes_available() -> bool:
|
|
214
|
+
try:
|
|
215
|
+
_load_library()
|
|
216
|
+
except CudaCtypesUnavailable:
|
|
217
|
+
return False
|
|
218
|
+
return True
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
def release_cuda_device_memory() -> None:
|
|
222
|
+
library = _load_library()
|
|
223
|
+
status = library.gds_cuda_release_device_memory()
|
|
224
|
+
if status != 0:
|
|
225
|
+
message = library.gds_cuda_last_error()
|
|
226
|
+
raise CudaCtypesUnavailable(message.decode("utf-8") if message else "CUDA device memory release failed")
|
|
227
|
+
|
|
228
|
+
|
|
229
|
+
def compare_files_cuda(old_path: str | Path, new_path: str | Path) -> CudaByteCompare:
|
|
230
|
+
old_size = Path(old_path).stat().st_size
|
|
231
|
+
new_size = Path(new_path).stat().st_size
|
|
232
|
+
if old_size != new_size:
|
|
233
|
+
return CudaByteCompare(old_size=old_size, new_size=new_size, same=False)
|
|
234
|
+
|
|
235
|
+
library = _load_library()
|
|
236
|
+
result = _CByteCompareResult()
|
|
237
|
+
status = library.gds_cuda_compare_files(
|
|
238
|
+
str(old_path).encode("utf-8"),
|
|
239
|
+
str(new_path).encode("utf-8"),
|
|
240
|
+
ctypes.byref(result),
|
|
241
|
+
)
|
|
242
|
+
if status != 0:
|
|
243
|
+
message = library.gds_cuda_last_error()
|
|
244
|
+
raise CudaCtypesUnavailable(message.decode("utf-8") if message else "CUDA byte compare failed")
|
|
245
|
+
return CudaByteCompare(old_size=int(result.old_size), new_size=int(result.new_size), same=bool(result.same))
|
|
246
|
+
|
|
247
|
+
|
|
248
|
+
def run_cuda_polygon_record_delta(old_buffer: PolygonBuffer, new_buffer: PolygonBuffer) -> CudaPolygonRecordDelta:
|
|
249
|
+
library = _load_library()
|
|
250
|
+
old_packed = _pack_polygon_record_buffer(old_buffer)
|
|
251
|
+
new_packed = _pack_polygon_record_buffer(new_buffer)
|
|
252
|
+
result = _CPolygonRecordDeltaResult()
|
|
253
|
+
status = library.gds_cuda_polygon_record_delta(
|
|
254
|
+
_ptr_i64(old_packed["vertices"]),
|
|
255
|
+
ctypes.c_uint64(old_buffer.vertex_count),
|
|
256
|
+
_ptr_u64(old_packed["offsets"]),
|
|
257
|
+
_ptr_i32(old_packed["layers"]),
|
|
258
|
+
_ptr_i32(old_packed["datatypes"]),
|
|
259
|
+
_ptr_i64(old_packed["bboxes"]),
|
|
260
|
+
_ptr_u32(old_packed["flags"]),
|
|
261
|
+
ctypes.c_uint64(old_buffer.polygon_count),
|
|
262
|
+
_ptr_i64(new_packed["vertices"]),
|
|
263
|
+
ctypes.c_uint64(new_buffer.vertex_count),
|
|
264
|
+
_ptr_u64(new_packed["offsets"]),
|
|
265
|
+
_ptr_i32(new_packed["layers"]),
|
|
266
|
+
_ptr_i32(new_packed["datatypes"]),
|
|
267
|
+
_ptr_i64(new_packed["bboxes"]),
|
|
268
|
+
_ptr_u32(new_packed["flags"]),
|
|
269
|
+
ctypes.c_uint64(new_buffer.polygon_count),
|
|
270
|
+
ctypes.byref(result),
|
|
271
|
+
)
|
|
272
|
+
if status != 0:
|
|
273
|
+
message = library.gds_cuda_last_error()
|
|
274
|
+
raise CudaCtypesUnavailable(message.decode("utf-8") if message else "CUDA polygon record delta failed")
|
|
275
|
+
try:
|
|
276
|
+
count = int(result.record_count)
|
|
277
|
+
return CudaPolygonRecordDelta(
|
|
278
|
+
layers=_copy_result_array(result.layers, count, np.int32),
|
|
279
|
+
datatypes=_copy_result_array(result.datatypes, count, np.int32),
|
|
280
|
+
directions=_copy_result_array(result.directions, count, np.uint8),
|
|
281
|
+
counts=_copy_result_array(result.counts, count, np.uint64),
|
|
282
|
+
bboxes=_copy_result_array(result.bboxes, count * 4, np.int64).reshape((count, 4)),
|
|
283
|
+
vertex_counts=_copy_result_array(result.vertex_counts, count, np.uint64),
|
|
284
|
+
flags=_copy_result_array(result.flags, count, np.uint32),
|
|
285
|
+
hashes=_copy_result_array(result.hashes, count * 2, np.uint64).reshape((count, 2)),
|
|
286
|
+
representative_indices=_copy_result_array(result.representative_indices, count, np.uint64),
|
|
287
|
+
old_input_count=int(result.old_input_count),
|
|
288
|
+
new_input_count=int(result.new_input_count),
|
|
289
|
+
matched_record_count=int(result.matched_record_count),
|
|
290
|
+
reduced_key_count=int(result.reduced_key_count),
|
|
291
|
+
cuda_kernel_count=int(result.cuda_kernel_count),
|
|
292
|
+
cuda_event_elapsed_s=float(result.cuda_event_elapsed_ms) / 1000.0,
|
|
293
|
+
transferred_bytes=_polygon_record_transfer_bytes(old_packed, new_packed),
|
|
294
|
+
)
|
|
295
|
+
finally:
|
|
296
|
+
library.gds_cuda_free_polygon_record_delta_result(ctypes.byref(result))
|
|
297
|
+
|
|
298
|
+
|
|
299
|
+
def run_cuda_polygon_record_delta_from_hashes(old_buffer: PolygonBuffer, new_buffer: PolygonBuffer) -> CudaPolygonRecordDelta:
|
|
300
|
+
if old_buffer.polygon_hashes is None or new_buffer.polygon_hashes is None:
|
|
301
|
+
return run_cuda_polygon_record_delta(old_buffer, new_buffer)
|
|
302
|
+
library = _load_library()
|
|
303
|
+
old_packed = _pack_polygon_hash_record_buffer(old_buffer)
|
|
304
|
+
new_packed = _pack_polygon_hash_record_buffer(new_buffer)
|
|
305
|
+
result = _CPolygonRecordDeltaResult()
|
|
306
|
+
status = library.gds_cuda_polygon_record_delta_from_hashes(
|
|
307
|
+
_ptr_u64(old_packed["offsets"]),
|
|
308
|
+
_ptr_i32(old_packed["layers"]),
|
|
309
|
+
_ptr_i32(old_packed["datatypes"]),
|
|
310
|
+
_ptr_i64(old_packed["bboxes"]),
|
|
311
|
+
_ptr_u32(old_packed["flags"]),
|
|
312
|
+
_ptr_u64(old_packed["hashes"]),
|
|
313
|
+
ctypes.c_uint64(old_buffer.polygon_count),
|
|
314
|
+
_ptr_u64(new_packed["offsets"]),
|
|
315
|
+
_ptr_i32(new_packed["layers"]),
|
|
316
|
+
_ptr_i32(new_packed["datatypes"]),
|
|
317
|
+
_ptr_i64(new_packed["bboxes"]),
|
|
318
|
+
_ptr_u32(new_packed["flags"]),
|
|
319
|
+
_ptr_u64(new_packed["hashes"]),
|
|
320
|
+
ctypes.c_uint64(new_buffer.polygon_count),
|
|
321
|
+
ctypes.byref(result),
|
|
322
|
+
)
|
|
323
|
+
if status != 0:
|
|
324
|
+
message = library.gds_cuda_last_error()
|
|
325
|
+
raise CudaCtypesUnavailable(message.decode("utf-8") if message else "CUDA polygon hash record delta failed")
|
|
326
|
+
try:
|
|
327
|
+
count = int(result.record_count)
|
|
328
|
+
return CudaPolygonRecordDelta(
|
|
329
|
+
layers=_copy_result_array(result.layers, count, np.int32),
|
|
330
|
+
datatypes=_copy_result_array(result.datatypes, count, np.int32),
|
|
331
|
+
directions=_copy_result_array(result.directions, count, np.uint8),
|
|
332
|
+
counts=_copy_result_array(result.counts, count, np.uint64),
|
|
333
|
+
bboxes=_copy_result_array(result.bboxes, count * 4, np.int64).reshape((count, 4)),
|
|
334
|
+
vertex_counts=_copy_result_array(result.vertex_counts, count, np.uint64),
|
|
335
|
+
flags=_copy_result_array(result.flags, count, np.uint32),
|
|
336
|
+
hashes=_copy_result_array(result.hashes, count * 2, np.uint64).reshape((count, 2)),
|
|
337
|
+
representative_indices=_copy_result_array(result.representative_indices, count, np.uint64),
|
|
338
|
+
old_input_count=int(result.old_input_count),
|
|
339
|
+
new_input_count=int(result.new_input_count),
|
|
340
|
+
matched_record_count=int(result.matched_record_count),
|
|
341
|
+
reduced_key_count=int(result.reduced_key_count),
|
|
342
|
+
cuda_kernel_count=int(result.cuda_kernel_count),
|
|
343
|
+
cuda_event_elapsed_s=float(result.cuda_event_elapsed_ms) / 1000.0,
|
|
344
|
+
transferred_bytes=_polygon_record_transfer_bytes(old_packed, new_packed),
|
|
345
|
+
)
|
|
346
|
+
finally:
|
|
347
|
+
library.gds_cuda_free_polygon_record_delta_result(ctypes.byref(result))
|
|
348
|
+
|
|
349
|
+
|
|
350
|
+
def run_cuda_rect_exact_areas(
|
|
351
|
+
old_rects: np.ndarray,
|
|
352
|
+
new_rects: np.ndarray,
|
|
353
|
+
old_present: np.ndarray,
|
|
354
|
+
new_present: np.ndarray,
|
|
355
|
+
) -> np.ndarray:
|
|
356
|
+
library = _load_library()
|
|
357
|
+
old_rects = np.ascontiguousarray(old_rects, dtype=np.int64)
|
|
358
|
+
new_rects = np.ascontiguousarray(new_rects, dtype=np.int64)
|
|
359
|
+
old_present = np.ascontiguousarray(old_present, dtype=np.uint8)
|
|
360
|
+
new_present = np.ascontiguousarray(new_present, dtype=np.uint8)
|
|
361
|
+
if old_rects.ndim != 2 or old_rects.shape[1] != 4:
|
|
362
|
+
raise ValueError("old_rects must have shape (N, 4)")
|
|
363
|
+
if new_rects.shape != old_rects.shape:
|
|
364
|
+
raise ValueError("new_rects must match old_rects shape")
|
|
365
|
+
count = old_rects.shape[0]
|
|
366
|
+
if old_present.shape != (count,) or new_present.shape != (count,):
|
|
367
|
+
raise ValueError("presence arrays must have shape (N,)")
|
|
368
|
+
out = np.empty((count, 3), dtype=np.int64)
|
|
369
|
+
status = library.gds_cuda_rect_exact_areas(
|
|
370
|
+
_ptr_i64(old_rects),
|
|
371
|
+
_ptr_i64(new_rects),
|
|
372
|
+
old_present.ctypes.data_as(ctypes.POINTER(ctypes.c_uint8)),
|
|
373
|
+
new_present.ctypes.data_as(ctypes.POINTER(ctypes.c_uint8)),
|
|
374
|
+
ctypes.c_uint64(count),
|
|
375
|
+
_ptr_i64(out),
|
|
376
|
+
)
|
|
377
|
+
if status != 0:
|
|
378
|
+
message = library.gds_cuda_last_error()
|
|
379
|
+
raise CudaCtypesUnavailable(message.decode("utf-8") if message else "CUDA rect exact failed")
|
|
380
|
+
return out
|
|
381
|
+
|
|
382
|
+
|
|
383
|
+
def run_cuda_rect_exact_fragments(
|
|
384
|
+
old_rects: np.ndarray,
|
|
385
|
+
new_rects: np.ndarray,
|
|
386
|
+
old_present: np.ndarray,
|
|
387
|
+
new_present: np.ndarray,
|
|
388
|
+
) -> tuple[np.ndarray, np.ndarray, np.ndarray, np.ndarray]:
|
|
389
|
+
library = _load_library()
|
|
390
|
+
old_rects = np.ascontiguousarray(old_rects, dtype=np.int64)
|
|
391
|
+
new_rects = np.ascontiguousarray(new_rects, dtype=np.int64)
|
|
392
|
+
old_present = np.ascontiguousarray(old_present, dtype=np.uint8)
|
|
393
|
+
new_present = np.ascontiguousarray(new_present, dtype=np.uint8)
|
|
394
|
+
if old_rects.ndim != 2 or old_rects.shape[1] != 4:
|
|
395
|
+
raise ValueError("old_rects must have shape (N, 4)")
|
|
396
|
+
if new_rects.shape != old_rects.shape:
|
|
397
|
+
raise ValueError("new_rects must match old_rects shape")
|
|
398
|
+
count = old_rects.shape[0]
|
|
399
|
+
if old_present.shape != (count,) or new_present.shape != (count,):
|
|
400
|
+
raise ValueError("presence arrays must have shape (N,)")
|
|
401
|
+
rects = np.empty((count, 8, 4), dtype=np.int64)
|
|
402
|
+
counts = np.empty((count, 2), dtype=np.uint8)
|
|
403
|
+
areas = np.empty((count, 3), dtype=np.int64)
|
|
404
|
+
status = library.gds_cuda_rect_exact_fragments(
|
|
405
|
+
_ptr_i64(old_rects),
|
|
406
|
+
_ptr_i64(new_rects),
|
|
407
|
+
old_present.ctypes.data_as(ctypes.POINTER(ctypes.c_uint8)),
|
|
408
|
+
new_present.ctypes.data_as(ctypes.POINTER(ctypes.c_uint8)),
|
|
409
|
+
ctypes.c_uint64(count),
|
|
410
|
+
_ptr_i64(rects),
|
|
411
|
+
counts.ctypes.data_as(ctypes.POINTER(ctypes.c_uint8)),
|
|
412
|
+
_ptr_i64(areas),
|
|
413
|
+
)
|
|
414
|
+
if status != 0:
|
|
415
|
+
message = library.gds_cuda_last_error()
|
|
416
|
+
raise CudaCtypesUnavailable(message.decode("utf-8") if message else "CUDA rect exact fragments failed")
|
|
417
|
+
return rects, counts, areas
|
|
418
|
+
|
|
419
|
+
|
|
420
|
+
def run_cuda_rect_set_exact_fragments(
|
|
421
|
+
old_rects: np.ndarray,
|
|
422
|
+
new_rects: np.ndarray,
|
|
423
|
+
old_counts: np.ndarray,
|
|
424
|
+
new_counts: np.ndarray,
|
|
425
|
+
*,
|
|
426
|
+
max_fragments: int = 128,
|
|
427
|
+
) -> tuple[np.ndarray, np.ndarray, np.ndarray]:
|
|
428
|
+
library = _load_library()
|
|
429
|
+
old_rects = np.ascontiguousarray(old_rects, dtype=np.int64)
|
|
430
|
+
new_rects = np.ascontiguousarray(new_rects, dtype=np.int64)
|
|
431
|
+
old_counts = np.ascontiguousarray(old_counts, dtype=np.uint8)
|
|
432
|
+
new_counts = np.ascontiguousarray(new_counts, dtype=np.uint8)
|
|
433
|
+
if old_rects.ndim != 3 or old_rects.shape[2] != 4:
|
|
434
|
+
raise ValueError("old_rects must have shape (N, max_rects, 4)")
|
|
435
|
+
if new_rects.shape != old_rects.shape:
|
|
436
|
+
raise ValueError("new_rects must match old_rects shape")
|
|
437
|
+
task_count, max_rects, _ = old_rects.shape
|
|
438
|
+
if old_counts.shape != (task_count,) or new_counts.shape != (task_count,):
|
|
439
|
+
raise ValueError("count arrays must have shape (N,)")
|
|
440
|
+
if max_rects <= 0 or max_rects > 16:
|
|
441
|
+
raise ValueError("max_rects must be between 1 and 16")
|
|
442
|
+
if max_fragments <= 0 or max_fragments > 4096:
|
|
443
|
+
raise ValueError("max_fragments must be between 1 and 4096")
|
|
444
|
+
rects = np.empty((task_count, 2, max_fragments, 4), dtype=np.int64)
|
|
445
|
+
counts = np.empty((task_count, 2), dtype=np.uint16)
|
|
446
|
+
areas = np.empty((task_count, 3), dtype=np.int64)
|
|
447
|
+
status = library.gds_cuda_rect_set_exact_fragments(
|
|
448
|
+
_ptr_i64(old_rects),
|
|
449
|
+
_ptr_i64(new_rects),
|
|
450
|
+
old_counts.ctypes.data_as(ctypes.POINTER(ctypes.c_uint8)),
|
|
451
|
+
new_counts.ctypes.data_as(ctypes.POINTER(ctypes.c_uint8)),
|
|
452
|
+
ctypes.c_uint64(task_count),
|
|
453
|
+
ctypes.c_uint16(max_rects),
|
|
454
|
+
ctypes.c_uint16(max_fragments),
|
|
455
|
+
_ptr_i64(rects),
|
|
456
|
+
counts.ctypes.data_as(ctypes.POINTER(ctypes.c_uint16)),
|
|
457
|
+
_ptr_i64(areas),
|
|
458
|
+
)
|
|
459
|
+
if status != 0:
|
|
460
|
+
message = library.gds_cuda_last_error()
|
|
461
|
+
raise CudaCtypesUnavailable(message.decode("utf-8") if message else "CUDA rect set exact fragments failed")
|
|
462
|
+
return rects, counts, areas
|
|
463
|
+
|
|
464
|
+
|
|
465
|
+
def run_cuda_polygon_component_areas(components: tuple[PolygonComponent, ...]) -> np.ndarray:
|
|
466
|
+
library = _load_library()
|
|
467
|
+
packed = _pack_polygon_components(components)
|
|
468
|
+
component_count = len(components)
|
|
469
|
+
areas = np.zeros((component_count, 3), dtype=np.int64)
|
|
470
|
+
status = library.gds_cuda_polygon_component_areas(
|
|
471
|
+
_ptr_i64(packed["vertices"]),
|
|
472
|
+
ctypes.c_uint64(len(packed["vertices"]) // 2),
|
|
473
|
+
_ptr_u64(packed["polygon_offsets"]),
|
|
474
|
+
packed["polygon_sides"].ctypes.data_as(ctypes.POINTER(ctypes.c_uint8)),
|
|
475
|
+
ctypes.c_uint64(len(packed["polygon_sides"])),
|
|
476
|
+
_ptr_u64(packed["component_offsets"]),
|
|
477
|
+
ctypes.c_uint64(component_count),
|
|
478
|
+
_ptr_i64(areas),
|
|
479
|
+
)
|
|
480
|
+
if status != 0:
|
|
481
|
+
message = library.gds_cuda_last_error()
|
|
482
|
+
raise CudaCtypesUnavailable(message.decode("utf-8") if message else "CUDA polygon component areas failed")
|
|
483
|
+
return areas
|
|
484
|
+
|
|
485
|
+
|
|
486
|
+
def run_cuda_polygon_component_fragments(
|
|
487
|
+
components: tuple[PolygonComponent, ...],
|
|
488
|
+
*,
|
|
489
|
+
max_fragments: int = 4096,
|
|
490
|
+
) -> tuple[np.ndarray, np.ndarray, np.ndarray]:
|
|
491
|
+
library = _load_library()
|
|
492
|
+
if max_fragments <= 0 or max_fragments > 65535:
|
|
493
|
+
raise ValueError("max_fragments must be between 1 and 65535")
|
|
494
|
+
packed = _pack_polygon_components(components)
|
|
495
|
+
component_count = len(components)
|
|
496
|
+
quads = np.zeros((component_count, 2, max_fragments, 4, 2), dtype=np.int64)
|
|
497
|
+
fragment_areas = np.zeros((component_count, 2, max_fragments), dtype=np.int64)
|
|
498
|
+
counts = np.zeros((component_count, 2), dtype=np.uint16)
|
|
499
|
+
areas = np.zeros((component_count, 3), dtype=np.int64)
|
|
500
|
+
status = library.gds_cuda_polygon_component_fragments(
|
|
501
|
+
_ptr_i64(packed["vertices"]),
|
|
502
|
+
ctypes.c_uint64(len(packed["vertices"]) // 2),
|
|
503
|
+
_ptr_u64(packed["polygon_offsets"]),
|
|
504
|
+
packed["polygon_sides"].ctypes.data_as(ctypes.POINTER(ctypes.c_uint8)),
|
|
505
|
+
ctypes.c_uint64(len(packed["polygon_sides"])),
|
|
506
|
+
_ptr_u64(packed["component_offsets"]),
|
|
507
|
+
ctypes.c_uint64(component_count),
|
|
508
|
+
ctypes.c_uint16(max_fragments),
|
|
509
|
+
_ptr_i64(quads),
|
|
510
|
+
_ptr_i64(fragment_areas),
|
|
511
|
+
counts.ctypes.data_as(ctypes.POINTER(ctypes.c_uint16)),
|
|
512
|
+
_ptr_i64(areas),
|
|
513
|
+
)
|
|
514
|
+
if status != 0:
|
|
515
|
+
message = library.gds_cuda_last_error()
|
|
516
|
+
raise CudaCtypesUnavailable(message.decode("utf-8") if message else "CUDA polygon component fragments failed")
|
|
517
|
+
return quads, fragment_areas, counts, areas
|
|
518
|
+
|
|
519
|
+
|
|
520
|
+
def run_cuda_one_sided_boundary_edges(
|
|
521
|
+
vertices_xy: np.ndarray,
|
|
522
|
+
polygon_offsets: np.ndarray,
|
|
523
|
+
bboxes: np.ndarray,
|
|
524
|
+
signed_twice_areas: np.ndarray,
|
|
525
|
+
segment_points: np.ndarray,
|
|
526
|
+
segment_polygon_indices: np.ndarray,
|
|
527
|
+
) -> np.ndarray:
|
|
528
|
+
library = _load_library()
|
|
529
|
+
vertices_xy = np.ascontiguousarray(vertices_xy, dtype=np.int64)
|
|
530
|
+
polygon_offsets = np.ascontiguousarray(polygon_offsets, dtype=np.uint64)
|
|
531
|
+
bboxes = np.ascontiguousarray(bboxes, dtype=np.int64)
|
|
532
|
+
signed_twice_areas = np.ascontiguousarray(signed_twice_areas, dtype=np.int64)
|
|
533
|
+
segment_points = np.ascontiguousarray(segment_points, dtype=np.float64)
|
|
534
|
+
segment_polygon_indices = np.ascontiguousarray(segment_polygon_indices, dtype=np.uint32)
|
|
535
|
+
if vertices_xy.ndim != 2 or vertices_xy.shape[1] != 2:
|
|
536
|
+
raise ValueError("vertices_xy must have shape (N, 2)")
|
|
537
|
+
if polygon_offsets.ndim != 1 or len(polygon_offsets) < 1:
|
|
538
|
+
raise ValueError("polygon_offsets must be a non-empty vector")
|
|
539
|
+
polygon_count = len(polygon_offsets) - 1
|
|
540
|
+
if bboxes.shape != (polygon_count, 4):
|
|
541
|
+
raise ValueError("bboxes must have shape (polygon_count, 4)")
|
|
542
|
+
if signed_twice_areas.shape != (polygon_count,):
|
|
543
|
+
raise ValueError("signed_twice_areas must have shape (polygon_count,)")
|
|
544
|
+
if segment_points.ndim != 2 or segment_points.shape[1] != 4:
|
|
545
|
+
raise ValueError("segment_points must have shape (segment_count, 4)")
|
|
546
|
+
segment_count = len(segment_points)
|
|
547
|
+
if segment_polygon_indices.shape != (segment_count,):
|
|
548
|
+
raise ValueError("segment_polygon_indices must have shape (segment_count,)")
|
|
549
|
+
out_edges = np.zeros((segment_count, 4), dtype=np.float64)
|
|
550
|
+
out_keep = np.zeros((segment_count,), dtype=np.uint8)
|
|
551
|
+
status = library.gds_cuda_one_sided_boundary_edges(
|
|
552
|
+
_ptr_i64(vertices_xy.reshape((-1,))),
|
|
553
|
+
ctypes.c_uint64(len(vertices_xy)),
|
|
554
|
+
_ptr_u64(polygon_offsets),
|
|
555
|
+
_ptr_i64(bboxes.reshape((-1,))),
|
|
556
|
+
_ptr_i64(signed_twice_areas),
|
|
557
|
+
ctypes.c_uint64(polygon_count),
|
|
558
|
+
_ptr_f64(segment_points.reshape((-1,))),
|
|
559
|
+
segment_polygon_indices.ctypes.data_as(ctypes.POINTER(ctypes.c_uint32)),
|
|
560
|
+
ctypes.c_uint64(segment_count),
|
|
561
|
+
_ptr_f64(out_edges.reshape((-1,))),
|
|
562
|
+
out_keep.ctypes.data_as(ctypes.POINTER(ctypes.c_uint8)),
|
|
563
|
+
)
|
|
564
|
+
if status != 0:
|
|
565
|
+
message = library.gds_cuda_last_error()
|
|
566
|
+
raise CudaCtypesUnavailable(message.decode("utf-8") if message else "CUDA one-sided boundary edges failed")
|
|
567
|
+
return out_edges[out_keep.astype(bool)].reshape((-1, 2, 2))
|
|
568
|
+
|
|
569
|
+
|
|
570
|
+
def run_cuda_mixed_boundary_edges(
|
|
571
|
+
vertices_xy: np.ndarray,
|
|
572
|
+
polygon_offsets: np.ndarray,
|
|
573
|
+
bboxes: np.ndarray,
|
|
574
|
+
polygon_sides: np.ndarray,
|
|
575
|
+
segment_points: np.ndarray,
|
|
576
|
+
*,
|
|
577
|
+
direction: str,
|
|
578
|
+
) -> np.ndarray:
|
|
579
|
+
library = _load_library()
|
|
580
|
+
vertices_xy = np.ascontiguousarray(vertices_xy, dtype=np.int64)
|
|
581
|
+
polygon_offsets = np.ascontiguousarray(polygon_offsets, dtype=np.uint64)
|
|
582
|
+
bboxes = np.ascontiguousarray(bboxes, dtype=np.int64)
|
|
583
|
+
polygon_sides = np.ascontiguousarray(polygon_sides, dtype=np.uint8)
|
|
584
|
+
segment_points = np.ascontiguousarray(segment_points, dtype=np.float64)
|
|
585
|
+
if direction not in {"old", "new"}:
|
|
586
|
+
raise ValueError("direction must be 'old' or 'new'")
|
|
587
|
+
if vertices_xy.ndim != 2 or vertices_xy.shape[1] != 2:
|
|
588
|
+
raise ValueError("vertices_xy must have shape (N, 2)")
|
|
589
|
+
if polygon_offsets.ndim != 1 or len(polygon_offsets) < 1:
|
|
590
|
+
raise ValueError("polygon_offsets must be a non-empty vector")
|
|
591
|
+
polygon_count = len(polygon_offsets) - 1
|
|
592
|
+
if bboxes.shape != (polygon_count, 4):
|
|
593
|
+
raise ValueError("bboxes must have shape (polygon_count, 4)")
|
|
594
|
+
if polygon_sides.shape != (polygon_count,):
|
|
595
|
+
raise ValueError("polygon_sides must have shape (polygon_count,)")
|
|
596
|
+
if segment_points.ndim != 2 or segment_points.shape[1] != 4:
|
|
597
|
+
raise ValueError("segment_points must have shape (segment_count, 4)")
|
|
598
|
+
segment_count = len(segment_points)
|
|
599
|
+
out_edges = np.zeros((segment_count, 4), dtype=np.float64)
|
|
600
|
+
out_keep = np.zeros((segment_count,), dtype=np.uint8)
|
|
601
|
+
status = library.gds_cuda_mixed_boundary_edges(
|
|
602
|
+
_ptr_i64(vertices_xy.reshape((-1,))),
|
|
603
|
+
ctypes.c_uint64(len(vertices_xy)),
|
|
604
|
+
_ptr_u64(polygon_offsets),
|
|
605
|
+
_ptr_i64(bboxes.reshape((-1,))),
|
|
606
|
+
polygon_sides.ctypes.data_as(ctypes.POINTER(ctypes.c_uint8)),
|
|
607
|
+
ctypes.c_uint64(polygon_count),
|
|
608
|
+
_ptr_f64(segment_points.reshape((-1,))),
|
|
609
|
+
ctypes.c_uint64(segment_count),
|
|
610
|
+
ctypes.c_uint8(1 if direction == "old" else 2),
|
|
611
|
+
_ptr_f64(out_edges.reshape((-1,))),
|
|
612
|
+
out_keep.ctypes.data_as(ctypes.POINTER(ctypes.c_uint8)),
|
|
613
|
+
)
|
|
614
|
+
if status != 0:
|
|
615
|
+
message = library.gds_cuda_last_error()
|
|
616
|
+
raise CudaCtypesUnavailable(message.decode("utf-8") if message else "CUDA mixed boundary edges failed")
|
|
617
|
+
return out_edges[out_keep.astype(bool)].reshape((-1, 2, 2))
|
|
618
|
+
|
|
619
|
+
|
|
620
|
+
def run_cuda_one_sided_boundary_edges_unsplit_if_safe(
|
|
621
|
+
vertices_xy: np.ndarray,
|
|
622
|
+
polygon_offsets: np.ndarray,
|
|
623
|
+
bboxes: np.ndarray,
|
|
624
|
+
signed_twice_areas: np.ndarray,
|
|
625
|
+
segment_points: np.ndarray,
|
|
626
|
+
segment_polygon_indices: np.ndarray,
|
|
627
|
+
) -> tuple[np.ndarray, bool]:
|
|
628
|
+
library = _load_library()
|
|
629
|
+
vertices_xy = np.ascontiguousarray(vertices_xy, dtype=np.int64)
|
|
630
|
+
polygon_offsets = np.ascontiguousarray(polygon_offsets, dtype=np.uint64)
|
|
631
|
+
bboxes = np.ascontiguousarray(bboxes, dtype=np.int64)
|
|
632
|
+
signed_twice_areas = np.ascontiguousarray(signed_twice_areas, dtype=np.int64)
|
|
633
|
+
segment_points = np.ascontiguousarray(segment_points, dtype=np.float64)
|
|
634
|
+
segment_polygon_indices = np.ascontiguousarray(segment_polygon_indices, dtype=np.uint32)
|
|
635
|
+
if vertices_xy.ndim != 2 or vertices_xy.shape[1] != 2:
|
|
636
|
+
raise ValueError("vertices_xy must have shape (N, 2)")
|
|
637
|
+
if polygon_offsets.ndim != 1 or len(polygon_offsets) < 1:
|
|
638
|
+
raise ValueError("polygon_offsets must be a non-empty vector")
|
|
639
|
+
polygon_count = len(polygon_offsets) - 1
|
|
640
|
+
if bboxes.shape != (polygon_count, 4):
|
|
641
|
+
raise ValueError("bboxes must have shape (polygon_count, 4)")
|
|
642
|
+
if signed_twice_areas.shape != (polygon_count,):
|
|
643
|
+
raise ValueError("signed_twice_areas must have shape (polygon_count,)")
|
|
644
|
+
if segment_points.ndim != 2 or segment_points.shape[1] != 4:
|
|
645
|
+
raise ValueError("segment_points must have shape (segment_count, 4)")
|
|
646
|
+
segment_count = len(segment_points)
|
|
647
|
+
if segment_polygon_indices.shape != (segment_count,):
|
|
648
|
+
raise ValueError("segment_polygon_indices must have shape (segment_count,)")
|
|
649
|
+
out_edges = np.zeros((segment_count, 4), dtype=np.float64)
|
|
650
|
+
out_keep = np.zeros((segment_count,), dtype=np.uint8)
|
|
651
|
+
out_safe = ctypes.c_uint8(0)
|
|
652
|
+
status = library.gds_cuda_one_sided_boundary_edges_unsplit_if_safe(
|
|
653
|
+
_ptr_i64(vertices_xy.reshape((-1,))),
|
|
654
|
+
ctypes.c_uint64(len(vertices_xy)),
|
|
655
|
+
_ptr_u64(polygon_offsets),
|
|
656
|
+
_ptr_i64(bboxes.reshape((-1,))),
|
|
657
|
+
_ptr_i64(signed_twice_areas),
|
|
658
|
+
ctypes.c_uint64(polygon_count),
|
|
659
|
+
_ptr_f64(segment_points.reshape((-1,))),
|
|
660
|
+
segment_polygon_indices.ctypes.data_as(ctypes.POINTER(ctypes.c_uint32)),
|
|
661
|
+
ctypes.c_uint64(segment_count),
|
|
662
|
+
_ptr_f64(out_edges.reshape((-1,))),
|
|
663
|
+
out_keep.ctypes.data_as(ctypes.POINTER(ctypes.c_uint8)),
|
|
664
|
+
ctypes.byref(out_safe),
|
|
665
|
+
)
|
|
666
|
+
if status != 0:
|
|
667
|
+
message = library.gds_cuda_last_error()
|
|
668
|
+
raise CudaCtypesUnavailable(message.decode("utf-8") if message else "CUDA one-sided unsplit boundary edges failed")
|
|
669
|
+
return out_edges[out_keep.astype(bool)].reshape((-1, 2, 2)), bool(out_safe.value)
|
|
670
|
+
|
|
671
|
+
|
|
672
|
+
def run_cuda_boundary_loops_from_edges(edges: np.ndarray) -> CudaBoundaryLoopResult:
|
|
673
|
+
library = _load_library()
|
|
674
|
+
edges = np.ascontiguousarray(edges, dtype=np.int64)
|
|
675
|
+
if edges.ndim != 2 or edges.shape[1] != 4:
|
|
676
|
+
raise ValueError("edges must have shape (N, 4)")
|
|
677
|
+
result = _CBoundaryLoopResult()
|
|
678
|
+
status = library.gds_cuda_boundary_loops_from_edges(
|
|
679
|
+
_ptr_i64(edges.reshape((-1,))),
|
|
680
|
+
ctypes.c_uint64(len(edges)),
|
|
681
|
+
ctypes.byref(result),
|
|
682
|
+
)
|
|
683
|
+
if status != 0:
|
|
684
|
+
message = library.gds_cuda_last_error()
|
|
685
|
+
raise CudaCtypesUnavailable(message.decode("utf-8") if message else "CUDA boundary loop tracing failed")
|
|
686
|
+
try:
|
|
687
|
+
count = int(result.fragment_count)
|
|
688
|
+
vertex_count = int(result.vertex_count)
|
|
689
|
+
vertices = _copy_result_array(result.vertices_xy, vertex_count * 2, np.int64).reshape((vertex_count, 2))
|
|
690
|
+
offsets = _copy_result_array(result.fragment_offsets, count + 1, np.uint64)
|
|
691
|
+
bboxes = _copy_result_array(result.bboxes, count * 4, np.int64).reshape((count, 4))
|
|
692
|
+
twice_areas = _copy_result_array(result.twice_areas, count, np.int64)
|
|
693
|
+
fragments = []
|
|
694
|
+
for index in range(count):
|
|
695
|
+
start = int(offsets[index])
|
|
696
|
+
stop = int(offsets[index + 1])
|
|
697
|
+
points = tuple((int(x), int(y)) for x, y in vertices[start:stop])
|
|
698
|
+
bbox = tuple(int(value) for value in bboxes[index])
|
|
699
|
+
fragments.append(DiffFragment(points=points, bbox=bbox, twice_area=int(twice_areas[index])))
|
|
700
|
+
return CudaBoundaryLoopResult(
|
|
701
|
+
fragments=tuple(sorted(fragments, key=lambda fragment: (fragment.bbox, fragment.twice_area, fragment.points))),
|
|
702
|
+
dropped_zero_area_loop_count=int(result.dropped_zero_area_loop_count),
|
|
703
|
+
cuda_kernel_count=int(result.cuda_kernel_count),
|
|
704
|
+
cuda_event_elapsed_s=float(result.cuda_event_elapsed_ms) / 1000.0,
|
|
705
|
+
transferred_bytes=int(edges.nbytes),
|
|
706
|
+
)
|
|
707
|
+
finally:
|
|
708
|
+
library.gds_cuda_free_boundary_loop_result(ctypes.byref(result))
|
|
709
|
+
|
|
710
|
+
|
|
711
|
+
def run_cuda_axis_aligned_boundary_edges_from_fragments(fragments: tuple[DiffFragment, ...]) -> CudaBoundaryEdgesResult:
|
|
712
|
+
library = _load_library()
|
|
713
|
+
vertices, offsets = _pack_diff_fragments(fragments)
|
|
714
|
+
result = _CBoundaryEdgesResult()
|
|
715
|
+
status = library.gds_cuda_axis_aligned_boundary_edges_from_polygons(
|
|
716
|
+
_ptr_i64(vertices.reshape((-1,))),
|
|
717
|
+
_ptr_u64(offsets),
|
|
718
|
+
ctypes.c_uint64(len(fragments)),
|
|
719
|
+
ctypes.byref(result),
|
|
720
|
+
)
|
|
721
|
+
if status != 0:
|
|
722
|
+
message = library.gds_cuda_last_error()
|
|
723
|
+
raise CudaCtypesUnavailable(message.decode("utf-8") if message else "CUDA axis boundary edge assembly failed")
|
|
724
|
+
try:
|
|
725
|
+
count = int(result.edge_count)
|
|
726
|
+
return CudaBoundaryEdgesResult(
|
|
727
|
+
edges=_copy_result_array(result.edges, count * 4, np.int64).reshape((count, 4)),
|
|
728
|
+
cuda_kernel_count=int(result.cuda_kernel_count),
|
|
729
|
+
cuda_event_elapsed_s=float(result.cuda_event_elapsed_ms) / 1000.0,
|
|
730
|
+
transferred_bytes=int(vertices.nbytes + offsets.nbytes),
|
|
731
|
+
)
|
|
732
|
+
finally:
|
|
733
|
+
library.gds_cuda_free_boundary_edges_result(ctypes.byref(result))
|
|
734
|
+
|
|
735
|
+
|
|
736
|
+
def run_cuda_split_free_boundary_edges_from_fragments(fragments: tuple[DiffFragment, ...]) -> CudaBoundaryEdgesResult:
|
|
737
|
+
library = _load_library()
|
|
738
|
+
vertices, offsets = _pack_diff_fragments(fragments)
|
|
739
|
+
result = _CBoundaryEdgesResult()
|
|
740
|
+
status = library.gds_cuda_split_free_boundary_edges_from_polygons(
|
|
741
|
+
_ptr_i64(vertices.reshape((-1,))),
|
|
742
|
+
_ptr_u64(offsets),
|
|
743
|
+
ctypes.c_uint64(len(fragments)),
|
|
744
|
+
ctypes.byref(result),
|
|
745
|
+
)
|
|
746
|
+
if status != 0:
|
|
747
|
+
message = library.gds_cuda_last_error()
|
|
748
|
+
raise CudaCtypesUnavailable(message.decode("utf-8") if message else "CUDA split-free boundary edge assembly failed")
|
|
749
|
+
try:
|
|
750
|
+
count = int(result.edge_count)
|
|
751
|
+
return CudaBoundaryEdgesResult(
|
|
752
|
+
edges=_copy_result_array(result.edges, count * 4, np.int64).reshape((count, 4)),
|
|
753
|
+
cuda_kernel_count=int(result.cuda_kernel_count),
|
|
754
|
+
cuda_event_elapsed_s=float(result.cuda_event_elapsed_ms) / 1000.0,
|
|
755
|
+
transferred_bytes=int(vertices.nbytes + offsets.nbytes),
|
|
756
|
+
)
|
|
757
|
+
finally:
|
|
758
|
+
library.gds_cuda_free_boundary_edges_result(ctypes.byref(result))
|
|
759
|
+
|
|
760
|
+
|
|
761
|
+
def run_cuda_context_overlap_pairs(
|
|
762
|
+
component_bboxes: np.ndarray,
|
|
763
|
+
component_layers: np.ndarray,
|
|
764
|
+
component_datatypes: np.ndarray,
|
|
765
|
+
polygon_bboxes: np.ndarray,
|
|
766
|
+
polygon_layers: np.ndarray,
|
|
767
|
+
polygon_datatypes: np.ndarray,
|
|
768
|
+
) -> CudaContextOverlapPairs:
|
|
769
|
+
library = _load_library()
|
|
770
|
+
component_bboxes = np.ascontiguousarray(component_bboxes, dtype=np.int64)
|
|
771
|
+
component_layers = np.ascontiguousarray(component_layers, dtype=np.int32)
|
|
772
|
+
component_datatypes = np.ascontiguousarray(component_datatypes, dtype=np.int32)
|
|
773
|
+
polygon_bboxes = np.ascontiguousarray(polygon_bboxes, dtype=np.int64)
|
|
774
|
+
polygon_layers = np.ascontiguousarray(polygon_layers, dtype=np.int32)
|
|
775
|
+
polygon_datatypes = np.ascontiguousarray(polygon_datatypes, dtype=np.int32)
|
|
776
|
+
if component_bboxes.ndim != 2 or component_bboxes.shape[1] != 4:
|
|
777
|
+
raise ValueError("component_bboxes must have shape (N, 4)")
|
|
778
|
+
if polygon_bboxes.ndim != 2 or polygon_bboxes.shape[1] != 4:
|
|
779
|
+
raise ValueError("polygon_bboxes must have shape (M, 4)")
|
|
780
|
+
component_count = component_bboxes.shape[0]
|
|
781
|
+
polygon_count = polygon_bboxes.shape[0]
|
|
782
|
+
if component_layers.shape != (component_count,) or component_datatypes.shape != (component_count,):
|
|
783
|
+
raise ValueError("component layer/datatype arrays must have shape (N,)")
|
|
784
|
+
if polygon_layers.shape != (polygon_count,) or polygon_datatypes.shape != (polygon_count,):
|
|
785
|
+
raise ValueError("polygon layer/datatype arrays must have shape (M,)")
|
|
786
|
+
result = _CContextOverlapPairsResult()
|
|
787
|
+
status = library.gds_cuda_context_overlap_pairs(
|
|
788
|
+
_ptr_i64(component_bboxes.reshape((-1,))),
|
|
789
|
+
_ptr_i32(component_layers),
|
|
790
|
+
_ptr_i32(component_datatypes),
|
|
791
|
+
ctypes.c_uint64(component_count),
|
|
792
|
+
_ptr_i64(polygon_bboxes.reshape((-1,))),
|
|
793
|
+
_ptr_i32(polygon_layers),
|
|
794
|
+
_ptr_i32(polygon_datatypes),
|
|
795
|
+
ctypes.c_uint64(polygon_count),
|
|
796
|
+
ctypes.byref(result),
|
|
797
|
+
)
|
|
798
|
+
if status != 0:
|
|
799
|
+
message = library.gds_cuda_last_error()
|
|
800
|
+
raise CudaCtypesUnavailable(message.decode("utf-8") if message else "CUDA context overlap pairs failed")
|
|
801
|
+
try:
|
|
802
|
+
count = int(result.pair_count)
|
|
803
|
+
return CudaContextOverlapPairs(
|
|
804
|
+
component_indices=_copy_result_array(result.component_indices, count, np.uint64),
|
|
805
|
+
polygon_indices=_copy_result_array(result.polygon_indices, count, np.uint64),
|
|
806
|
+
cuda_kernel_count=int(result.cuda_kernel_count),
|
|
807
|
+
cuda_event_elapsed_s=float(result.cuda_event_elapsed_ms) / 1000.0,
|
|
808
|
+
transferred_bytes=int(
|
|
809
|
+
component_bboxes.nbytes
|
|
810
|
+
+ component_layers.nbytes
|
|
811
|
+
+ component_datatypes.nbytes
|
|
812
|
+
+ polygon_bboxes.nbytes
|
|
813
|
+
+ polygon_layers.nbytes
|
|
814
|
+
+ polygon_datatypes.nbytes
|
|
815
|
+
),
|
|
816
|
+
)
|
|
817
|
+
finally:
|
|
818
|
+
library.gds_cuda_free_context_overlap_pairs_result(ctypes.byref(result))
|
|
819
|
+
|
|
820
|
+
|
|
821
|
+
def _candidate_tiles_from_result(result: _CResult) -> tuple[TileKey, ...]:
|
|
822
|
+
if result.candidate_count == 0:
|
|
823
|
+
return ()
|
|
824
|
+
array = np.ctypeslib.as_array(result.candidate_tiles, shape=(int(result.candidate_count) * 4,))
|
|
825
|
+
tiles = [
|
|
826
|
+
TileKey(LayerSpec(int(array[index]), int(array[index + 1])), int(array[index + 2]), int(array[index + 3]))
|
|
827
|
+
for index in range(0, len(array), 4)
|
|
828
|
+
]
|
|
829
|
+
return tuple(tiles)
|
|
830
|
+
|
|
831
|
+
|
|
832
|
+
def _oversized_from_result(result: _CResult) -> tuple[OversizedPolygon, ...]:
|
|
833
|
+
if result.oversized_count == 0:
|
|
834
|
+
return ()
|
|
835
|
+
array = np.ctypeslib.as_array(result.oversized, shape=(int(result.oversized_count) * 8,))
|
|
836
|
+
items = [
|
|
837
|
+
OversizedPolygon(
|
|
838
|
+
layer=LayerSpec(int(array[index]), int(array[index + 1])),
|
|
839
|
+
canonical_id=int(array[index + 2]),
|
|
840
|
+
side_mask=int(array[index + 3]),
|
|
841
|
+
tile_bbox=(int(array[index + 4]), int(array[index + 5]), int(array[index + 6]), int(array[index + 7])),
|
|
842
|
+
)
|
|
843
|
+
for index in range(0, len(array), 8)
|
|
844
|
+
]
|
|
845
|
+
return tuple(items)
|
|
846
|
+
|
|
847
|
+
|
|
848
|
+
def _pack_buffer(buffer: PolygonBuffer, canonical_ids) -> _PackedBuffer:
|
|
849
|
+
ids = np.ascontiguousarray(canonical_ids, dtype=np.uint64)
|
|
850
|
+
if len(ids) != buffer.polygon_count:
|
|
851
|
+
raise ValueError("canonical id vector length must match polygon count")
|
|
852
|
+
layers = np.empty(buffer.polygon_count, dtype=np.int32)
|
|
853
|
+
datatypes = np.empty(buffer.polygon_count, dtype=np.int32)
|
|
854
|
+
for index, layer_id in enumerate(buffer.layer_ids):
|
|
855
|
+
layer = buffer.layer_table.layers[int(layer_id)]
|
|
856
|
+
layers[index] = layer.layer
|
|
857
|
+
datatypes[index] = layer.datatype
|
|
858
|
+
return _PackedBuffer(
|
|
859
|
+
bboxes=np.ascontiguousarray(buffer.bboxes, dtype=np.int64),
|
|
860
|
+
layers=np.ascontiguousarray(layers, dtype=np.int32),
|
|
861
|
+
datatypes=np.ascontiguousarray(datatypes, dtype=np.int32),
|
|
862
|
+
canonical_ids=ids,
|
|
863
|
+
)
|
|
864
|
+
|
|
865
|
+
|
|
866
|
+
def _skip_exact_tile_bbox_matches(old: _PackedBuffer, new: _PackedBuffer, config: TileConfig) -> tuple[_PackedBuffer, _PackedBuffer, int]:
|
|
867
|
+
side_masks: dict[tuple[int, int, int, int, int, int, int], int] = {}
|
|
868
|
+
old_first: dict[tuple[int, int, int, int, int, int, int], int] = {}
|
|
869
|
+
new_first: dict[tuple[int, int, int, int, int, int, int], int] = {}
|
|
870
|
+
for index, key in enumerate(_tile_bbox_keys(old, config.tile_size)):
|
|
871
|
+
side_masks[key] = side_masks.get(key, 0) | 1
|
|
872
|
+
old_first.setdefault(key, index)
|
|
873
|
+
for index, key in enumerate(_tile_bbox_keys(new, config.tile_size)):
|
|
874
|
+
side_masks[key] = side_masks.get(key, 0) | 2
|
|
875
|
+
new_first.setdefault(key, index)
|
|
876
|
+
old_keep = [old_first[key] for key, mask in side_masks.items() if mask == 1]
|
|
877
|
+
new_keep = [new_first[key] for key, mask in side_masks.items() if mask == 2]
|
|
878
|
+
skipped = sum(1 for mask in side_masks.values() if mask == 3)
|
|
879
|
+
return _take_packed(old, old_keep), _take_packed(new, new_keep), skipped
|
|
880
|
+
|
|
881
|
+
|
|
882
|
+
def _tile_bbox_keys(packed: _PackedBuffer, tile_size: int):
|
|
883
|
+
tx0 = np.floor_divide(packed.bboxes[:, 0], tile_size)
|
|
884
|
+
ty0 = np.floor_divide(packed.bboxes[:, 1], tile_size)
|
|
885
|
+
tx1 = np.floor_divide(packed.bboxes[:, 2], tile_size)
|
|
886
|
+
ty1 = np.floor_divide(packed.bboxes[:, 3], tile_size)
|
|
887
|
+
for index in range(len(packed.canonical_ids)):
|
|
888
|
+
yield (
|
|
889
|
+
int(packed.layers[index]),
|
|
890
|
+
int(packed.datatypes[index]),
|
|
891
|
+
int(packed.canonical_ids[index]),
|
|
892
|
+
int(tx0[index]),
|
|
893
|
+
int(ty0[index]),
|
|
894
|
+
int(tx1[index]),
|
|
895
|
+
int(ty1[index]),
|
|
896
|
+
)
|
|
897
|
+
|
|
898
|
+
|
|
899
|
+
def _take_packed(packed: _PackedBuffer, indices: list[int]) -> _PackedBuffer:
|
|
900
|
+
if not indices:
|
|
901
|
+
return _PackedBuffer(
|
|
902
|
+
bboxes=np.empty((0, 4), dtype=np.int64),
|
|
903
|
+
layers=np.empty((0,), dtype=np.int32),
|
|
904
|
+
datatypes=np.empty((0,), dtype=np.int32),
|
|
905
|
+
canonical_ids=np.empty((0,), dtype=np.uint64),
|
|
906
|
+
)
|
|
907
|
+
index_array = np.asarray(indices, dtype=np.int64)
|
|
908
|
+
return _PackedBuffer(
|
|
909
|
+
bboxes=np.ascontiguousarray(packed.bboxes[index_array], dtype=np.int64),
|
|
910
|
+
layers=np.ascontiguousarray(packed.layers[index_array], dtype=np.int32),
|
|
911
|
+
datatypes=np.ascontiguousarray(packed.datatypes[index_array], dtype=np.int32),
|
|
912
|
+
canonical_ids=np.ascontiguousarray(packed.canonical_ids[index_array], dtype=np.uint64),
|
|
913
|
+
)
|
|
914
|
+
|
|
915
|
+
|
|
916
|
+
def _ptr_i64(array: np.ndarray):
|
|
917
|
+
return array.ctypes.data_as(ctypes.POINTER(ctypes.c_int64))
|
|
918
|
+
|
|
919
|
+
|
|
920
|
+
def _ptr_i32(array: np.ndarray):
|
|
921
|
+
return array.ctypes.data_as(ctypes.POINTER(ctypes.c_int32))
|
|
922
|
+
|
|
923
|
+
|
|
924
|
+
def _ptr_u64(array: np.ndarray):
|
|
925
|
+
return array.ctypes.data_as(ctypes.POINTER(ctypes.c_uint64))
|
|
926
|
+
|
|
927
|
+
|
|
928
|
+
def _ptr_u32(array: np.ndarray):
|
|
929
|
+
return array.ctypes.data_as(ctypes.POINTER(ctypes.c_uint32))
|
|
930
|
+
|
|
931
|
+
|
|
932
|
+
def _ptr_f64(array: np.ndarray):
|
|
933
|
+
return array.ctypes.data_as(ctypes.POINTER(ctypes.c_double))
|
|
934
|
+
|
|
935
|
+
|
|
936
|
+
def _copy_result_array(pointer, count: int, dtype) -> np.ndarray:
|
|
937
|
+
if count == 0:
|
|
938
|
+
return np.empty((0,), dtype=dtype)
|
|
939
|
+
if not pointer:
|
|
940
|
+
raise CudaCtypesUnavailable("CUDA result returned null output array")
|
|
941
|
+
ctype = np.ctypeslib.as_ctypes_type(dtype)
|
|
942
|
+
array = ctypes.cast(pointer, ctypes.POINTER(ctype * count)).contents
|
|
943
|
+
return np.frombuffer(array, dtype=dtype).copy()
|
|
944
|
+
|
|
945
|
+
|
|
946
|
+
def _pack_polygon_record_buffer(buffer: PolygonBuffer) -> dict[str, np.ndarray]:
|
|
947
|
+
layer_values = np.asarray([layer.layer for layer in buffer.layer_table.layers], dtype=np.int32)
|
|
948
|
+
datatype_values = np.asarray([layer.datatype for layer in buffer.layer_table.layers], dtype=np.int32)
|
|
949
|
+
layers = np.ascontiguousarray(layer_values[buffer.layer_ids], dtype=np.int32)
|
|
950
|
+
datatypes = np.ascontiguousarray(datatype_values[buffer.layer_ids], dtype=np.int32)
|
|
951
|
+
return {
|
|
952
|
+
"vertices": np.ascontiguousarray(buffer.vertices_xy.reshape((-1,)), dtype=np.int64),
|
|
953
|
+
"offsets": np.ascontiguousarray(buffer.polygon_offsets, dtype=np.uint64),
|
|
954
|
+
"layers": layers,
|
|
955
|
+
"datatypes": datatypes,
|
|
956
|
+
"bboxes": np.ascontiguousarray(buffer.bboxes.reshape((-1,)), dtype=np.int64),
|
|
957
|
+
"flags": np.ascontiguousarray(buffer.flags, dtype=np.uint32),
|
|
958
|
+
}
|
|
959
|
+
|
|
960
|
+
|
|
961
|
+
def _pack_polygon_hash_record_buffer(buffer: PolygonBuffer) -> dict[str, np.ndarray]:
|
|
962
|
+
if buffer.polygon_hashes is None:
|
|
963
|
+
raise CudaCtypesUnavailable("native polygon hashes are unavailable")
|
|
964
|
+
layer_values = np.asarray([layer.layer for layer in buffer.layer_table.layers], dtype=np.int32)
|
|
965
|
+
datatype_values = np.asarray([layer.datatype for layer in buffer.layer_table.layers], dtype=np.int32)
|
|
966
|
+
layers = np.ascontiguousarray(layer_values[buffer.layer_ids], dtype=np.int32)
|
|
967
|
+
datatypes = np.ascontiguousarray(datatype_values[buffer.layer_ids], dtype=np.int32)
|
|
968
|
+
return {
|
|
969
|
+
"offsets": np.ascontiguousarray(buffer.polygon_offsets, dtype=np.uint64),
|
|
970
|
+
"layers": layers,
|
|
971
|
+
"datatypes": datatypes,
|
|
972
|
+
"bboxes": np.ascontiguousarray(buffer.bboxes.reshape((-1,)), dtype=np.int64),
|
|
973
|
+
"flags": np.ascontiguousarray(buffer.flags, dtype=np.uint32),
|
|
974
|
+
"hashes": np.ascontiguousarray(buffer.polygon_hashes.reshape((-1,)), dtype=np.uint64),
|
|
975
|
+
}
|
|
976
|
+
|
|
977
|
+
|
|
978
|
+
def _pack_polygon_components(components: tuple[PolygonComponent, ...]) -> dict[str, np.ndarray]:
|
|
979
|
+
vertices: list[tuple[int, int]] = []
|
|
980
|
+
polygon_offsets = [0]
|
|
981
|
+
polygon_sides: list[int] = []
|
|
982
|
+
component_offsets = [0]
|
|
983
|
+
polygon_count = 0
|
|
984
|
+
for component in components:
|
|
985
|
+
for polygon in component.polygons:
|
|
986
|
+
vertices.extend(polygon.fragment.points)
|
|
987
|
+
polygon_offsets.append(len(vertices))
|
|
988
|
+
polygon_sides.append(1 if polygon.side == "old" else 2)
|
|
989
|
+
polygon_count += 1
|
|
990
|
+
component_offsets.append(polygon_count)
|
|
991
|
+
return {
|
|
992
|
+
"vertices": np.ascontiguousarray(np.asarray(vertices, dtype=np.int64).reshape((-1,)), dtype=np.int64),
|
|
993
|
+
"polygon_offsets": np.ascontiguousarray(np.asarray(polygon_offsets, dtype=np.uint64), dtype=np.uint64),
|
|
994
|
+
"polygon_sides": np.ascontiguousarray(np.asarray(polygon_sides, dtype=np.uint8), dtype=np.uint8),
|
|
995
|
+
"component_offsets": np.ascontiguousarray(np.asarray(component_offsets, dtype=np.uint64), dtype=np.uint64),
|
|
996
|
+
}
|
|
997
|
+
|
|
998
|
+
|
|
999
|
+
def _pack_diff_fragments(fragments: tuple[DiffFragment, ...]) -> tuple[np.ndarray, np.ndarray]:
|
|
1000
|
+
vertices: list[tuple[int, int]] = []
|
|
1001
|
+
offsets = [0]
|
|
1002
|
+
for fragment in fragments:
|
|
1003
|
+
vertices.extend(fragment.points)
|
|
1004
|
+
offsets.append(len(vertices))
|
|
1005
|
+
return (
|
|
1006
|
+
np.ascontiguousarray(np.asarray(vertices, dtype=np.int64).reshape((-1, 2)), dtype=np.int64),
|
|
1007
|
+
np.ascontiguousarray(np.asarray(offsets, dtype=np.uint64), dtype=np.uint64),
|
|
1008
|
+
)
|
|
1009
|
+
|
|
1010
|
+
|
|
1011
|
+
def _polygon_record_transfer_bytes(*packed_buffers: dict[str, np.ndarray]) -> int:
|
|
1012
|
+
return sum(int(array.nbytes) for packed in packed_buffers for array in packed.values())
|
|
1013
|
+
|
|
1014
|
+
|
|
1015
|
+
@lru_cache(maxsize=1)
|
|
1016
|
+
def _load_library() -> ctypes.CDLL:
|
|
1017
|
+
path = _library_path()
|
|
1018
|
+
library = ctypes.CDLL(str(path))
|
|
1019
|
+
library.gds_cuda_prefilter.argtypes = [
|
|
1020
|
+
ctypes.POINTER(ctypes.c_int64),
|
|
1021
|
+
ctypes.POINTER(ctypes.c_int32),
|
|
1022
|
+
ctypes.POINTER(ctypes.c_int32),
|
|
1023
|
+
ctypes.POINTER(ctypes.c_uint64),
|
|
1024
|
+
ctypes.c_uint64,
|
|
1025
|
+
ctypes.POINTER(ctypes.c_int64),
|
|
1026
|
+
ctypes.POINTER(ctypes.c_int32),
|
|
1027
|
+
ctypes.POINTER(ctypes.c_int32),
|
|
1028
|
+
ctypes.POINTER(ctypes.c_uint64),
|
|
1029
|
+
ctypes.c_uint64,
|
|
1030
|
+
ctypes.c_int64,
|
|
1031
|
+
ctypes.c_uint64,
|
|
1032
|
+
ctypes.POINTER(_CResult),
|
|
1033
|
+
]
|
|
1034
|
+
library.gds_cuda_prefilter.restype = ctypes.c_int
|
|
1035
|
+
library.gds_cuda_last_error.argtypes = []
|
|
1036
|
+
library.gds_cuda_last_error.restype = ctypes.c_char_p
|
|
1037
|
+
library.gds_cuda_release_device_memory.argtypes = []
|
|
1038
|
+
library.gds_cuda_release_device_memory.restype = ctypes.c_int
|
|
1039
|
+
library.gds_cuda_free_result.argtypes = [ctypes.POINTER(_CResult)]
|
|
1040
|
+
library.gds_cuda_free_result.restype = None
|
|
1041
|
+
library.gds_cuda_compare_files.argtypes = [
|
|
1042
|
+
ctypes.c_char_p,
|
|
1043
|
+
ctypes.c_char_p,
|
|
1044
|
+
ctypes.POINTER(_CByteCompareResult),
|
|
1045
|
+
]
|
|
1046
|
+
library.gds_cuda_compare_files.restype = ctypes.c_int
|
|
1047
|
+
library.gds_cuda_polygon_record_delta.argtypes = [
|
|
1048
|
+
ctypes.POINTER(ctypes.c_int64),
|
|
1049
|
+
ctypes.c_uint64,
|
|
1050
|
+
ctypes.POINTER(ctypes.c_uint64),
|
|
1051
|
+
ctypes.POINTER(ctypes.c_int32),
|
|
1052
|
+
ctypes.POINTER(ctypes.c_int32),
|
|
1053
|
+
ctypes.POINTER(ctypes.c_int64),
|
|
1054
|
+
ctypes.POINTER(ctypes.c_uint32),
|
|
1055
|
+
ctypes.c_uint64,
|
|
1056
|
+
ctypes.POINTER(ctypes.c_int64),
|
|
1057
|
+
ctypes.c_uint64,
|
|
1058
|
+
ctypes.POINTER(ctypes.c_uint64),
|
|
1059
|
+
ctypes.POINTER(ctypes.c_int32),
|
|
1060
|
+
ctypes.POINTER(ctypes.c_int32),
|
|
1061
|
+
ctypes.POINTER(ctypes.c_int64),
|
|
1062
|
+
ctypes.POINTER(ctypes.c_uint32),
|
|
1063
|
+
ctypes.c_uint64,
|
|
1064
|
+
ctypes.POINTER(_CPolygonRecordDeltaResult),
|
|
1065
|
+
]
|
|
1066
|
+
library.gds_cuda_polygon_record_delta.restype = ctypes.c_int
|
|
1067
|
+
library.gds_cuda_polygon_record_delta_from_hashes.argtypes = [
|
|
1068
|
+
ctypes.POINTER(ctypes.c_uint64),
|
|
1069
|
+
ctypes.POINTER(ctypes.c_int32),
|
|
1070
|
+
ctypes.POINTER(ctypes.c_int32),
|
|
1071
|
+
ctypes.POINTER(ctypes.c_int64),
|
|
1072
|
+
ctypes.POINTER(ctypes.c_uint32),
|
|
1073
|
+
ctypes.POINTER(ctypes.c_uint64),
|
|
1074
|
+
ctypes.c_uint64,
|
|
1075
|
+
ctypes.POINTER(ctypes.c_uint64),
|
|
1076
|
+
ctypes.POINTER(ctypes.c_int32),
|
|
1077
|
+
ctypes.POINTER(ctypes.c_int32),
|
|
1078
|
+
ctypes.POINTER(ctypes.c_int64),
|
|
1079
|
+
ctypes.POINTER(ctypes.c_uint32),
|
|
1080
|
+
ctypes.POINTER(ctypes.c_uint64),
|
|
1081
|
+
ctypes.c_uint64,
|
|
1082
|
+
ctypes.POINTER(_CPolygonRecordDeltaResult),
|
|
1083
|
+
]
|
|
1084
|
+
library.gds_cuda_polygon_record_delta_from_hashes.restype = ctypes.c_int
|
|
1085
|
+
library.gds_cuda_free_polygon_record_delta_result.argtypes = [ctypes.POINTER(_CPolygonRecordDeltaResult)]
|
|
1086
|
+
library.gds_cuda_free_polygon_record_delta_result.restype = None
|
|
1087
|
+
library.gds_cuda_boundary_loops_from_edges.argtypes = [
|
|
1088
|
+
ctypes.POINTER(ctypes.c_int64),
|
|
1089
|
+
ctypes.c_uint64,
|
|
1090
|
+
ctypes.POINTER(_CBoundaryLoopResult),
|
|
1091
|
+
]
|
|
1092
|
+
library.gds_cuda_boundary_loops_from_edges.restype = ctypes.c_int
|
|
1093
|
+
library.gds_cuda_free_boundary_loop_result.argtypes = [ctypes.POINTER(_CBoundaryLoopResult)]
|
|
1094
|
+
library.gds_cuda_free_boundary_loop_result.restype = None
|
|
1095
|
+
library.gds_cuda_axis_aligned_boundary_edges_from_polygons.argtypes = [
|
|
1096
|
+
ctypes.POINTER(ctypes.c_int64),
|
|
1097
|
+
ctypes.POINTER(ctypes.c_uint64),
|
|
1098
|
+
ctypes.c_uint64,
|
|
1099
|
+
ctypes.POINTER(_CBoundaryEdgesResult),
|
|
1100
|
+
]
|
|
1101
|
+
library.gds_cuda_axis_aligned_boundary_edges_from_polygons.restype = ctypes.c_int
|
|
1102
|
+
library.gds_cuda_split_free_boundary_edges_from_polygons.argtypes = [
|
|
1103
|
+
ctypes.POINTER(ctypes.c_int64),
|
|
1104
|
+
ctypes.POINTER(ctypes.c_uint64),
|
|
1105
|
+
ctypes.c_uint64,
|
|
1106
|
+
ctypes.POINTER(_CBoundaryEdgesResult),
|
|
1107
|
+
]
|
|
1108
|
+
library.gds_cuda_split_free_boundary_edges_from_polygons.restype = ctypes.c_int
|
|
1109
|
+
library.gds_cuda_free_boundary_edges_result.argtypes = [ctypes.POINTER(_CBoundaryEdgesResult)]
|
|
1110
|
+
library.gds_cuda_free_boundary_edges_result.restype = None
|
|
1111
|
+
library.gds_cuda_context_overlap_pairs.argtypes = [
|
|
1112
|
+
ctypes.POINTER(ctypes.c_int64),
|
|
1113
|
+
ctypes.POINTER(ctypes.c_int32),
|
|
1114
|
+
ctypes.POINTER(ctypes.c_int32),
|
|
1115
|
+
ctypes.c_uint64,
|
|
1116
|
+
ctypes.POINTER(ctypes.c_int64),
|
|
1117
|
+
ctypes.POINTER(ctypes.c_int32),
|
|
1118
|
+
ctypes.POINTER(ctypes.c_int32),
|
|
1119
|
+
ctypes.c_uint64,
|
|
1120
|
+
ctypes.POINTER(_CContextOverlapPairsResult),
|
|
1121
|
+
]
|
|
1122
|
+
library.gds_cuda_context_overlap_pairs.restype = ctypes.c_int
|
|
1123
|
+
library.gds_cuda_free_context_overlap_pairs_result.argtypes = [ctypes.POINTER(_CContextOverlapPairsResult)]
|
|
1124
|
+
library.gds_cuda_free_context_overlap_pairs_result.restype = None
|
|
1125
|
+
library.gds_cuda_rect_exact_areas.argtypes = [
|
|
1126
|
+
ctypes.POINTER(ctypes.c_int64),
|
|
1127
|
+
ctypes.POINTER(ctypes.c_int64),
|
|
1128
|
+
ctypes.POINTER(ctypes.c_uint8),
|
|
1129
|
+
ctypes.POINTER(ctypes.c_uint8),
|
|
1130
|
+
ctypes.c_uint64,
|
|
1131
|
+
ctypes.POINTER(ctypes.c_int64),
|
|
1132
|
+
]
|
|
1133
|
+
library.gds_cuda_rect_exact_areas.restype = ctypes.c_int
|
|
1134
|
+
library.gds_cuda_rect_exact_fragments.argtypes = [
|
|
1135
|
+
ctypes.POINTER(ctypes.c_int64),
|
|
1136
|
+
ctypes.POINTER(ctypes.c_int64),
|
|
1137
|
+
ctypes.POINTER(ctypes.c_uint8),
|
|
1138
|
+
ctypes.POINTER(ctypes.c_uint8),
|
|
1139
|
+
ctypes.c_uint64,
|
|
1140
|
+
ctypes.POINTER(ctypes.c_int64),
|
|
1141
|
+
ctypes.POINTER(ctypes.c_uint8),
|
|
1142
|
+
ctypes.POINTER(ctypes.c_int64),
|
|
1143
|
+
]
|
|
1144
|
+
library.gds_cuda_rect_exact_fragments.restype = ctypes.c_int
|
|
1145
|
+
library.gds_cuda_polygon_component_areas.argtypes = [
|
|
1146
|
+
ctypes.POINTER(ctypes.c_int64),
|
|
1147
|
+
ctypes.c_uint64,
|
|
1148
|
+
ctypes.POINTER(ctypes.c_uint64),
|
|
1149
|
+
ctypes.POINTER(ctypes.c_uint8),
|
|
1150
|
+
ctypes.c_uint64,
|
|
1151
|
+
ctypes.POINTER(ctypes.c_uint64),
|
|
1152
|
+
ctypes.c_uint64,
|
|
1153
|
+
ctypes.POINTER(ctypes.c_int64),
|
|
1154
|
+
]
|
|
1155
|
+
library.gds_cuda_polygon_component_areas.restype = ctypes.c_int
|
|
1156
|
+
library.gds_cuda_polygon_component_fragments.argtypes = [
|
|
1157
|
+
ctypes.POINTER(ctypes.c_int64),
|
|
1158
|
+
ctypes.c_uint64,
|
|
1159
|
+
ctypes.POINTER(ctypes.c_uint64),
|
|
1160
|
+
ctypes.POINTER(ctypes.c_uint8),
|
|
1161
|
+
ctypes.c_uint64,
|
|
1162
|
+
ctypes.POINTER(ctypes.c_uint64),
|
|
1163
|
+
ctypes.c_uint64,
|
|
1164
|
+
ctypes.c_uint16,
|
|
1165
|
+
ctypes.POINTER(ctypes.c_int64),
|
|
1166
|
+
ctypes.POINTER(ctypes.c_int64),
|
|
1167
|
+
ctypes.POINTER(ctypes.c_uint16),
|
|
1168
|
+
ctypes.POINTER(ctypes.c_int64),
|
|
1169
|
+
]
|
|
1170
|
+
library.gds_cuda_polygon_component_fragments.restype = ctypes.c_int
|
|
1171
|
+
library.gds_cuda_one_sided_boundary_edges.argtypes = [
|
|
1172
|
+
ctypes.POINTER(ctypes.c_int64),
|
|
1173
|
+
ctypes.c_uint64,
|
|
1174
|
+
ctypes.POINTER(ctypes.c_uint64),
|
|
1175
|
+
ctypes.POINTER(ctypes.c_int64),
|
|
1176
|
+
ctypes.POINTER(ctypes.c_int64),
|
|
1177
|
+
ctypes.c_uint64,
|
|
1178
|
+
ctypes.POINTER(ctypes.c_double),
|
|
1179
|
+
ctypes.POINTER(ctypes.c_uint32),
|
|
1180
|
+
ctypes.c_uint64,
|
|
1181
|
+
ctypes.POINTER(ctypes.c_double),
|
|
1182
|
+
ctypes.POINTER(ctypes.c_uint8),
|
|
1183
|
+
]
|
|
1184
|
+
library.gds_cuda_one_sided_boundary_edges.restype = ctypes.c_int
|
|
1185
|
+
library.gds_cuda_mixed_boundary_edges.argtypes = [
|
|
1186
|
+
ctypes.POINTER(ctypes.c_int64),
|
|
1187
|
+
ctypes.c_uint64,
|
|
1188
|
+
ctypes.POINTER(ctypes.c_uint64),
|
|
1189
|
+
ctypes.POINTER(ctypes.c_int64),
|
|
1190
|
+
ctypes.POINTER(ctypes.c_uint8),
|
|
1191
|
+
ctypes.c_uint64,
|
|
1192
|
+
ctypes.POINTER(ctypes.c_double),
|
|
1193
|
+
ctypes.c_uint64,
|
|
1194
|
+
ctypes.c_uint8,
|
|
1195
|
+
ctypes.POINTER(ctypes.c_double),
|
|
1196
|
+
ctypes.POINTER(ctypes.c_uint8),
|
|
1197
|
+
]
|
|
1198
|
+
library.gds_cuda_mixed_boundary_edges.restype = ctypes.c_int
|
|
1199
|
+
library.gds_cuda_one_sided_boundary_edges_unsplit_if_safe.argtypes = [
|
|
1200
|
+
ctypes.POINTER(ctypes.c_int64),
|
|
1201
|
+
ctypes.c_uint64,
|
|
1202
|
+
ctypes.POINTER(ctypes.c_uint64),
|
|
1203
|
+
ctypes.POINTER(ctypes.c_int64),
|
|
1204
|
+
ctypes.POINTER(ctypes.c_int64),
|
|
1205
|
+
ctypes.c_uint64,
|
|
1206
|
+
ctypes.POINTER(ctypes.c_double),
|
|
1207
|
+
ctypes.POINTER(ctypes.c_uint32),
|
|
1208
|
+
ctypes.c_uint64,
|
|
1209
|
+
ctypes.POINTER(ctypes.c_double),
|
|
1210
|
+
ctypes.POINTER(ctypes.c_uint8),
|
|
1211
|
+
ctypes.POINTER(ctypes.c_uint8),
|
|
1212
|
+
]
|
|
1213
|
+
library.gds_cuda_one_sided_boundary_edges_unsplit_if_safe.restype = ctypes.c_int
|
|
1214
|
+
library.gds_cuda_rect_set_exact_fragments.argtypes = [
|
|
1215
|
+
ctypes.POINTER(ctypes.c_int64),
|
|
1216
|
+
ctypes.POINTER(ctypes.c_int64),
|
|
1217
|
+
ctypes.POINTER(ctypes.c_uint8),
|
|
1218
|
+
ctypes.POINTER(ctypes.c_uint8),
|
|
1219
|
+
ctypes.c_uint64,
|
|
1220
|
+
ctypes.c_uint16,
|
|
1221
|
+
ctypes.c_uint16,
|
|
1222
|
+
ctypes.POINTER(ctypes.c_int64),
|
|
1223
|
+
ctypes.POINTER(ctypes.c_uint16),
|
|
1224
|
+
ctypes.POINTER(ctypes.c_int64),
|
|
1225
|
+
]
|
|
1226
|
+
library.gds_cuda_rect_set_exact_fragments.restype = ctypes.c_int
|
|
1227
|
+
return library
|
|
1228
|
+
|
|
1229
|
+
|
|
1230
|
+
def _build_library(path: Path) -> None:
|
|
1231
|
+
source = _source_path()
|
|
1232
|
+
architecture_flags = nvcc_architecture_flags()
|
|
1233
|
+
path.parent.mkdir(parents=True, exist_ok=True)
|
|
1234
|
+
nvcc = resolve_nvcc_path()
|
|
1235
|
+
if nvcc is None:
|
|
1236
|
+
raise CudaCtypesUnavailable("nvcc is not available; install the CUDA toolkit or set CUDA_HOME")
|
|
1237
|
+
command = [
|
|
1238
|
+
nvcc,
|
|
1239
|
+
"-std=c++17",
|
|
1240
|
+
"-O3",
|
|
1241
|
+
"--shared",
|
|
1242
|
+
"-Xcompiler",
|
|
1243
|
+
"-fPIC",
|
|
1244
|
+
*architecture_flags,
|
|
1245
|
+
str(source),
|
|
1246
|
+
"-o",
|
|
1247
|
+
str(path),
|
|
1248
|
+
]
|
|
1249
|
+
try:
|
|
1250
|
+
completed = subprocess.run(command, check=False, text=True, capture_output=True)
|
|
1251
|
+
except FileNotFoundError as exc:
|
|
1252
|
+
raise CudaCtypesUnavailable("nvcc is not available") from exc
|
|
1253
|
+
if completed.returncode != 0:
|
|
1254
|
+
raise CudaCtypesUnavailable((completed.stderr or completed.stdout).strip() or "nvcc failed")
|
|
1255
|
+
|
|
1256
|
+
|
|
1257
|
+
def _library_path() -> Path:
|
|
1258
|
+
build_key = _cuda_build_key()
|
|
1259
|
+
return ensure_cached_native_library(
|
|
1260
|
+
"_gds_compare_cuda_ctypes.so",
|
|
1261
|
+
sources=(_source_path(),),
|
|
1262
|
+
build_key=build_key,
|
|
1263
|
+
builder=_build_library,
|
|
1264
|
+
)
|
|
1265
|
+
|
|
1266
|
+
|
|
1267
|
+
def prepared_cuda_library_path() -> Path:
|
|
1268
|
+
"""Return the current toolchain/device cache target without building it."""
|
|
1269
|
+
|
|
1270
|
+
return _prepared_cuda_library_path_cached(_cuda_build_key(), str(native_cache_dir()))
|
|
1271
|
+
|
|
1272
|
+
|
|
1273
|
+
@lru_cache(maxsize=16)
|
|
1274
|
+
def _prepared_cuda_library_path_cached(
|
|
1275
|
+
build_key: tuple[str, ...], cache_directory: str
|
|
1276
|
+
) -> Path:
|
|
1277
|
+
return content_addressed_library_path(
|
|
1278
|
+
"_gds_compare_cuda_ctypes.so", sources=(_source_path(),), build_key=build_key
|
|
1279
|
+
)
|
|
1280
|
+
|
|
1281
|
+
|
|
1282
|
+
def _cuda_build_key() -> tuple[str, ...]:
|
|
1283
|
+
return (
|
|
1284
|
+
resolve_nvcc_path() or "nvcc",
|
|
1285
|
+
"-std=c++17",
|
|
1286
|
+
"-O3",
|
|
1287
|
+
"--shared",
|
|
1288
|
+
"-Xcompiler",
|
|
1289
|
+
"-fPIC",
|
|
1290
|
+
*nvcc_architecture_flags(),
|
|
1291
|
+
f"NVCC_PREPEND_FLAGS={os.environ.get('NVCC_PREPEND_FLAGS', '')}",
|
|
1292
|
+
f"NVCC_APPEND_FLAGS={os.environ.get('NVCC_APPEND_FLAGS', '')}",
|
|
1293
|
+
)
|
|
1294
|
+
|
|
1295
|
+
|
|
1296
|
+
def _source_path() -> Path:
|
|
1297
|
+
return Path(__file__).resolve().parents[1] / "native_src" / "gdsdiff" / "cuda_ctypes.cu"
|