prefab 1.1.8__py3-none-any.whl → 1.2.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.
- prefab/__init__.py +1 -1
- prefab/device.py +54 -11
- prefab/predict.py +170 -1
- {prefab-1.1.8.dist-info → prefab-1.2.0.dist-info}/METADATA +3 -3
- prefab-1.2.0.dist-info/RECORD +14 -0
- prefab-1.1.8.dist-info/RECORD +0 -14
- {prefab-1.1.8.dist-info → prefab-1.2.0.dist-info}/WHEEL +0 -0
- {prefab-1.1.8.dist-info → prefab-1.2.0.dist-info}/entry_points.txt +0 -0
- {prefab-1.1.8.dist-info → prefab-1.2.0.dist-info}/licenses/LICENSE +0 -0
prefab/__init__.py
CHANGED
prefab/device.py
CHANGED
|
@@ -397,6 +397,54 @@ class Device(BaseModel):
|
|
|
397
397
|
semulated_array += np.random.normal(0, 0.03, semulated_array.shape)
|
|
398
398
|
return self.model_copy(update={"device_array": semulated_array})
|
|
399
399
|
|
|
400
|
+
def segment(
|
|
401
|
+
self,
|
|
402
|
+
model: Model,
|
|
403
|
+
gpu: bool = False,
|
|
404
|
+
) -> "Device":
|
|
405
|
+
"""
|
|
406
|
+
Segment a scanning electron microscope (SEM) image into a binary mask.
|
|
407
|
+
|
|
408
|
+
This method applies a specified machine learning model to transform a grayscale
|
|
409
|
+
SEM image into a binary mask, where 1 represents the device structure and 0
|
|
410
|
+
represents the background. This is useful for extracting the device geometry
|
|
411
|
+
from experimental SEM images for analysis or comparison with design intent.
|
|
412
|
+
|
|
413
|
+
Parameters
|
|
414
|
+
----------
|
|
415
|
+
model : Model
|
|
416
|
+
The model to use for segmentation, representing a specific fabrication
|
|
417
|
+
process and dataset. This model encapsulates details about the fabrication
|
|
418
|
+
foundry, process, material, technology, thickness, and sidewall presence, as
|
|
419
|
+
defined in `models.py`. Each model is associated with a version and dataset
|
|
420
|
+
that detail its creation and the data it was trained on, ensuring the
|
|
421
|
+
segmentation is tailored to specific fabrication parameters.
|
|
422
|
+
gpu : bool
|
|
423
|
+
If True, the prediction will be performed on a GPU. Defaults to False.
|
|
424
|
+
Note: The GPU option has more overhead and will take longer for small
|
|
425
|
+
devices, but will be faster for larger devices.
|
|
426
|
+
|
|
427
|
+
Returns
|
|
428
|
+
-------
|
|
429
|
+
Device
|
|
430
|
+
A new instance of the Device class with its geometry transformed into a
|
|
431
|
+
binary mask.
|
|
432
|
+
|
|
433
|
+
Raises
|
|
434
|
+
------
|
|
435
|
+
RuntimeError
|
|
436
|
+
If the prediction service returns an error or if the response from the
|
|
437
|
+
service cannot be processed correctly.
|
|
438
|
+
"""
|
|
439
|
+
segmented_array = predict_array(
|
|
440
|
+
device_array=self.normalize().device_array,
|
|
441
|
+
model=model,
|
|
442
|
+
model_type="b",
|
|
443
|
+
binarize=False,
|
|
444
|
+
gpu=gpu,
|
|
445
|
+
)
|
|
446
|
+
return self.model_copy(update={"device_array": segmented_array})
|
|
447
|
+
|
|
400
448
|
def to_ndarray(self) -> np.ndarray:
|
|
401
449
|
"""
|
|
402
450
|
Converts the device geometry to an ndarray.
|
|
@@ -413,22 +461,17 @@ class Device(BaseModel):
|
|
|
413
461
|
"""
|
|
414
462
|
device_array = np.copy(self.device_array)
|
|
415
463
|
buffer_thickness = self.buffer_spec.thickness
|
|
416
|
-
buffer_mode = self.buffer_spec.mode
|
|
417
464
|
|
|
418
|
-
crop_top = buffer_thickness["top"]
|
|
419
|
-
crop_bottom =
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
crop_left = buffer_thickness["left"] if buffer_mode["left"] == "constant" else 0
|
|
423
|
-
crop_right = (
|
|
424
|
-
buffer_thickness["right"] if buffer_mode["right"] == "constant" else 0
|
|
425
|
-
)
|
|
465
|
+
crop_top = buffer_thickness["top"]
|
|
466
|
+
crop_bottom = buffer_thickness["bottom"]
|
|
467
|
+
crop_left = buffer_thickness["left"]
|
|
468
|
+
crop_right = buffer_thickness["right"]
|
|
426
469
|
|
|
427
470
|
ndarray = device_array[
|
|
428
471
|
crop_top : device_array.shape[0] - crop_bottom,
|
|
429
472
|
crop_left : device_array.shape[1] - crop_right,
|
|
430
473
|
]
|
|
431
|
-
return
|
|
474
|
+
return ndarray
|
|
432
475
|
|
|
433
476
|
def to_img(self, img_path: str = "prefab_device.png"):
|
|
434
477
|
"""
|
|
@@ -525,7 +568,7 @@ class Device(BaseModel):
|
|
|
525
568
|
gdstk.Cell
|
|
526
569
|
The GDSTK cell object representing the device geometry.
|
|
527
570
|
"""
|
|
528
|
-
print(f"Creating cell '{cell_name}'...")
|
|
571
|
+
# print(f"Creating cell '{cell_name}'...")
|
|
529
572
|
gdstk_cell = self.flatten()._device_to_gdstk(
|
|
530
573
|
cell_name=cell_name,
|
|
531
574
|
gds_layer=gds_layer,
|
prefab/predict.py
CHANGED
|
@@ -5,6 +5,7 @@ import io
|
|
|
5
5
|
import json
|
|
6
6
|
import os
|
|
7
7
|
|
|
8
|
+
import gdstk
|
|
8
9
|
import numpy as np
|
|
9
10
|
import requests
|
|
10
11
|
import toml
|
|
@@ -17,7 +18,175 @@ from .geometry import binarize_hard
|
|
|
17
18
|
from .models import Model
|
|
18
19
|
|
|
19
20
|
BASE_ENDPOINT_URL = "https://prefab-photonics--predict"
|
|
20
|
-
ENDPOINT_VERSION = 2
|
|
21
|
+
ENDPOINT_VERSION = "2"
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def _predict_poly(
|
|
25
|
+
polygon_points: list,
|
|
26
|
+
model: Model,
|
|
27
|
+
model_type: str,
|
|
28
|
+
eta: float = 0.5,
|
|
29
|
+
) -> list:
|
|
30
|
+
"""
|
|
31
|
+
Predict the nanofabrication outcome for a list of polygons.
|
|
32
|
+
|
|
33
|
+
This function sends polygon data to the server, which uses a specified machine
|
|
34
|
+
learning model to predict the outcome of the nanofabrication process.
|
|
35
|
+
|
|
36
|
+
Parameters
|
|
37
|
+
----------
|
|
38
|
+
polygon_points : list
|
|
39
|
+
List of polygon points, where each polygon is a list of [x, y] coordinates.
|
|
40
|
+
model : Model
|
|
41
|
+
The model to use for prediction, representing a specific fabrication process and
|
|
42
|
+
dataset. This model encapsulates details about the fabrication foundry, process,
|
|
43
|
+
material, technology, thickness, and sidewall presence, as defined in
|
|
44
|
+
`models.py`. Each model is associated with a version and dataset that detail its
|
|
45
|
+
creation and the data it was trained on, ensuring the prediction is tailored to
|
|
46
|
+
specific fabrication parameters.
|
|
47
|
+
model_type : str
|
|
48
|
+
The type of model to use ('p' for prediction, 'c' for correction).
|
|
49
|
+
eta : float
|
|
50
|
+
The threshold value for binarization. Defaults to 0.5. Because intermediate
|
|
51
|
+
values cannot be preserved in the polygon data, the predicted polygons are
|
|
52
|
+
binarized using a threshold value of eta.
|
|
53
|
+
|
|
54
|
+
Returns
|
|
55
|
+
-------
|
|
56
|
+
list
|
|
57
|
+
List of predicted polygon points with channel information. Each polygon is a
|
|
58
|
+
dict with 'points' (list of coordinates) and 'channel' (int) keys.
|
|
59
|
+
|
|
60
|
+
Raises
|
|
61
|
+
------
|
|
62
|
+
ValueError
|
|
63
|
+
If the server returns an error or empty response.
|
|
64
|
+
requests.exceptions.RequestException
|
|
65
|
+
If the request to the prediction service fails.
|
|
66
|
+
json.JSONDecodeError
|
|
67
|
+
If the response cannot be parsed as JSON.
|
|
68
|
+
"""
|
|
69
|
+
predict_data = {
|
|
70
|
+
"polygons": polygon_points,
|
|
71
|
+
"model": model.to_json(),
|
|
72
|
+
"model_type": model_type,
|
|
73
|
+
"eta": eta,
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
endpoint_url = f"{BASE_ENDPOINT_URL}-poly-v{ENDPOINT_VERSION}.modal.run"
|
|
77
|
+
headers = _prepare_headers()
|
|
78
|
+
|
|
79
|
+
try:
|
|
80
|
+
response = requests.post(
|
|
81
|
+
endpoint_url, data=json.dumps(predict_data), headers=headers
|
|
82
|
+
)
|
|
83
|
+
response.raise_for_status()
|
|
84
|
+
|
|
85
|
+
if not response.content:
|
|
86
|
+
raise ValueError("Empty response received from server")
|
|
87
|
+
|
|
88
|
+
response_data = response.json()
|
|
89
|
+
|
|
90
|
+
if "polygons" in response_data:
|
|
91
|
+
polygons = response_data["polygons"]
|
|
92
|
+
if polygons and isinstance(polygons[0], dict) and "channel" in polygons[0]:
|
|
93
|
+
return polygons
|
|
94
|
+
else:
|
|
95
|
+
return [{"points": points, "channel": 0} for points in polygons]
|
|
96
|
+
else:
|
|
97
|
+
if "error" in response_data:
|
|
98
|
+
raise ValueError(f"Prediction error: {response_data['error']}")
|
|
99
|
+
return []
|
|
100
|
+
|
|
101
|
+
except requests.exceptions.RequestException as e:
|
|
102
|
+
print(f"Request failed: {str(e)}")
|
|
103
|
+
raise
|
|
104
|
+
except json.JSONDecodeError as e:
|
|
105
|
+
print(f"JSON decode error: {str(e)}")
|
|
106
|
+
raise
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def predict_gdstk(
|
|
110
|
+
gdstk_cell: gdstk.Cell,
|
|
111
|
+
model: Model,
|
|
112
|
+
model_type: str,
|
|
113
|
+
gds_layer: tuple[int, int] = (1, 0),
|
|
114
|
+
eta: float = 0.5,
|
|
115
|
+
) -> gdstk.Cell:
|
|
116
|
+
"""
|
|
117
|
+
Predict the nanofabrication outcome of a gdstk cell using a specified model.
|
|
118
|
+
|
|
119
|
+
This function extracts polygons from a gdstk cell, sends them to the prediction
|
|
120
|
+
server, and returns a new cell containing the predicted polygons.
|
|
121
|
+
|
|
122
|
+
Parameters
|
|
123
|
+
----------
|
|
124
|
+
gdstk_cell : gdstk.Cell
|
|
125
|
+
The gdstk.Cell object containing polygons to predict.
|
|
126
|
+
model : Model
|
|
127
|
+
The model to use for prediction, representing a specific fabrication process and
|
|
128
|
+
dataset. This model encapsulates details about the fabrication foundry, process,
|
|
129
|
+
material, technology, thickness, and sidewall presence, as defined in
|
|
130
|
+
`models.py`. Each model is associated with a version and dataset that detail its
|
|
131
|
+
creation and the data it was trained on, ensuring the prediction is tailored to
|
|
132
|
+
specific fabrication parameters.
|
|
133
|
+
model_type : str
|
|
134
|
+
The type of model to use ('p' for prediction, 'c' for correction).
|
|
135
|
+
gds_layer : tuple[int, int]
|
|
136
|
+
The layer and datatype to use within the GDSTK cell. Defaults to (1, 0).
|
|
137
|
+
eta : float
|
|
138
|
+
The threshold value for binarization. Defaults to 0.5. Because intermediate
|
|
139
|
+
values cannot be preserved in the polygon data, the predicted polygons are
|
|
140
|
+
binarized using a threshold value of eta.
|
|
141
|
+
|
|
142
|
+
Returns
|
|
143
|
+
-------
|
|
144
|
+
gdstk.Cell
|
|
145
|
+
A new gdstk cell containing the predicted polygons. For multi-level
|
|
146
|
+
predictions, each level's polygons will be placed on a different layer:
|
|
147
|
+
- Level 0: (layer, 99)
|
|
148
|
+
- Level 1: (layer, 100)
|
|
149
|
+
|
|
150
|
+
Raises
|
|
151
|
+
------
|
|
152
|
+
ValueError
|
|
153
|
+
If no polygons are found in the specified layer.
|
|
154
|
+
"""
|
|
155
|
+
polygons = gdstk_cell.get_polygons(layer=gds_layer[0], datatype=gds_layer[1])
|
|
156
|
+
if not polygons:
|
|
157
|
+
raise ValueError("No polygons found in the specified layer")
|
|
158
|
+
|
|
159
|
+
polygon_points = [polygon.points.tolist() for polygon in polygons]
|
|
160
|
+
|
|
161
|
+
predicted_polygon_data = _predict_poly(
|
|
162
|
+
polygon_points=polygon_points,
|
|
163
|
+
model=model,
|
|
164
|
+
model_type=model_type,
|
|
165
|
+
eta=eta,
|
|
166
|
+
)
|
|
167
|
+
|
|
168
|
+
result_cell = gdstk.Cell(f"{gdstk_cell.name}_predicted")
|
|
169
|
+
|
|
170
|
+
polygons_by_channel = {}
|
|
171
|
+
for polygon_data in predicted_polygon_data:
|
|
172
|
+
channel = polygon_data.get("channel", 0)
|
|
173
|
+
points = polygon_data.get("points", [])
|
|
174
|
+
|
|
175
|
+
if channel not in polygons_by_channel:
|
|
176
|
+
polygons_by_channel[channel] = []
|
|
177
|
+
|
|
178
|
+
polygons_by_channel[channel].append(points)
|
|
179
|
+
|
|
180
|
+
for channel, points_list in polygons_by_channel.items():
|
|
181
|
+
layer = gds_layer[0]
|
|
182
|
+
datatype = 99 + channel
|
|
183
|
+
|
|
184
|
+
for points in points_list:
|
|
185
|
+
points_array = np.array(points)
|
|
186
|
+
polygon = gdstk.Polygon(points_array, layer=layer, datatype=datatype)
|
|
187
|
+
result_cell.add(polygon)
|
|
188
|
+
|
|
189
|
+
return result_cell
|
|
21
190
|
|
|
22
191
|
|
|
23
192
|
def predict_array(
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: prefab
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.2.0
|
|
4
4
|
Summary: Artificial nanofabrication of integrated photonic circuits using deep learning
|
|
5
5
|
Project-URL: Homepage, https://prefabphotonics.com
|
|
6
6
|
Project-URL: Repository, https://github.com/PreFab-Photonics/PreFab
|
|
@@ -586,7 +586,7 @@ Before you can make PreFab requests, you will need to [create an account](https:
|
|
|
586
586
|
To link your account, you will need an token. You can do this by running the following command in your terminal. This will open a browser window where you can log in and authenticate your token.
|
|
587
587
|
|
|
588
588
|
```sh
|
|
589
|
-
|
|
589
|
+
prefab setup
|
|
590
590
|
```
|
|
591
591
|
|
|
592
592
|
### Guides
|
|
@@ -603,4 +603,4 @@ PreFab models are hosted on a [serverless cloud platform](https://modal.com/). P
|
|
|
603
603
|
|
|
604
604
|
## License
|
|
605
605
|
|
|
606
|
-
This project is licensed under the LGPL-2.1 license. ©
|
|
606
|
+
This project is licensed under the LGPL-2.1 license. © 2025 PreFab Photonics.
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
prefab/__init__.py,sha256=1aWNs6J8s6UlWOvSlZscZ1W_uZboUlkhVkmJCWvJ7RU,425
|
|
2
|
+
prefab/__main__.py,sha256=1eXWiEoG7eetJMm1qRbK2I5MnzuRgKIoQtBeT-Ps8es,3523
|
|
3
|
+
prefab/compare.py,sha256=0Xgp3tFuP4of-ce9Opc19p8i8lIyXkbVGLuwWBaHSeE,3486
|
|
4
|
+
prefab/device.py,sha256=ZuppXLmDPSfym37U25hSsNu6JNM8ujrr4Bxh3VH4-4s,56582
|
|
5
|
+
prefab/geometry.py,sha256=4fekWMlkdS_qlPNTdPXPhwKuQ5qdQ1Zjf8m9JKd1dA8,12049
|
|
6
|
+
prefab/models.py,sha256=waPNGtuISyY0f8cz7dnbD451CKYCt8EpPGt-4lSOPNU,2581
|
|
7
|
+
prefab/predict.py,sha256=I0gdO0nNAdNQn_ALrHxrDjmBgxIGpPyU_hQYtnF9hYU,17733
|
|
8
|
+
prefab/read.py,sha256=WNqC3xENlndzFwXeCF2E7H3Iq2dO_6rPEPZ58DuloqY,16259
|
|
9
|
+
prefab/shapes.py,sha256=58cyXFNh1kEErq2jEbGd3dWSediU1OSmor_FWwc1V8A,25098
|
|
10
|
+
prefab-1.2.0.dist-info/METADATA,sha256=rd9O4Q3C0adI7BYkkuiEwlqkGw6UlQMpSAFA5Qbuwd0,35025
|
|
11
|
+
prefab-1.2.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
12
|
+
prefab-1.2.0.dist-info/entry_points.txt,sha256=h1_A9O9F3NAIoKXD1RPb3Eo-WCSiHhMB_AnagBi6XTQ,48
|
|
13
|
+
prefab-1.2.0.dist-info/licenses/LICENSE,sha256=IMF9i4xIpgCADf0U-V1cuf9HBmqWQd3qtI3FSuyW4zE,26526
|
|
14
|
+
prefab-1.2.0.dist-info/RECORD,,
|
prefab-1.1.8.dist-info/RECORD
DELETED
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
prefab/__init__.py,sha256=V4m0w3VrKeMgeNeqyc_45E5KXH9fVOQKO5xyH42uDmg,425
|
|
2
|
-
prefab/__main__.py,sha256=1eXWiEoG7eetJMm1qRbK2I5MnzuRgKIoQtBeT-Ps8es,3523
|
|
3
|
-
prefab/compare.py,sha256=0Xgp3tFuP4of-ce9Opc19p8i8lIyXkbVGLuwWBaHSeE,3486
|
|
4
|
-
prefab/device.py,sha256=1rqs_VQ7am6W473C-EZTsPFDlqNIMMd26VZAUV1tNS0,54885
|
|
5
|
-
prefab/geometry.py,sha256=4fekWMlkdS_qlPNTdPXPhwKuQ5qdQ1Zjf8m9JKd1dA8,12049
|
|
6
|
-
prefab/models.py,sha256=waPNGtuISyY0f8cz7dnbD451CKYCt8EpPGt-4lSOPNU,2581
|
|
7
|
-
prefab/predict.py,sha256=h13523jasg1WbdiYbkXy43SWTGfQXjq6oEe0O8DT2U0,11731
|
|
8
|
-
prefab/read.py,sha256=WNqC3xENlndzFwXeCF2E7H3Iq2dO_6rPEPZ58DuloqY,16259
|
|
9
|
-
prefab/shapes.py,sha256=58cyXFNh1kEErq2jEbGd3dWSediU1OSmor_FWwc1V8A,25098
|
|
10
|
-
prefab-1.1.8.dist-info/METADATA,sha256=d5ide0FP2wUQALM_2SSFhA_BGU6s3waxWjtXwsbjivs,35036
|
|
11
|
-
prefab-1.1.8.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
12
|
-
prefab-1.1.8.dist-info/entry_points.txt,sha256=h1_A9O9F3NAIoKXD1RPb3Eo-WCSiHhMB_AnagBi6XTQ,48
|
|
13
|
-
prefab-1.1.8.dist-info/licenses/LICENSE,sha256=IMF9i4xIpgCADf0U-V1cuf9HBmqWQd3qtI3FSuyW4zE,26526
|
|
14
|
-
prefab-1.1.8.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|