prefab 1.4.2__py3-none-any.whl → 1.6.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/__main__.py +2 -2
- prefab/predict.py +140 -82
- {prefab-1.4.2.dist-info → prefab-1.6.0.dist-info}/METADATA +2 -6
- {prefab-1.4.2.dist-info → prefab-1.6.0.dist-info}/RECORD +8 -8
- {prefab-1.4.2.dist-info → prefab-1.6.0.dist-info}/WHEEL +0 -0
- {prefab-1.4.2.dist-info → prefab-1.6.0.dist-info}/entry_points.txt +0 -0
- {prefab-1.4.2.dist-info → prefab-1.6.0.dist-info}/licenses/LICENSE +0 -0
prefab/__init__.py
CHANGED
prefab/__main__.py
CHANGED
|
@@ -18,8 +18,8 @@ def store_jwt(jwt, refresh_token):
|
|
|
18
18
|
print(
|
|
19
19
|
f"Token successfully stored in {prefab_file_path}.\n\n"
|
|
20
20
|
"🎉 Welcome to PreFab.\n"
|
|
21
|
-
"See our examples at https://docs.prefabphotonics.com
|
|
22
|
-
"Reach out to us at
|
|
21
|
+
"See our examples at https://docs.prefabphotonics.com to start.\n"
|
|
22
|
+
"Reach out to us at support@prefabphotonics.com for support."
|
|
23
23
|
)
|
|
24
24
|
|
|
25
25
|
|
prefab/predict.py
CHANGED
|
@@ -3,12 +3,13 @@ Serverless prediction interface for nanofabrication modeling.
|
|
|
3
3
|
|
|
4
4
|
This module provides functions for predicting nanofabrication outcomes using machine
|
|
5
5
|
learning models hosted on a serverless platform. It supports multiple input formats
|
|
6
|
-
(ndarrays, polygons, GDSII files) and model types (prediction
|
|
7
|
-
|
|
8
|
-
|
|
6
|
+
(ndarrays, polygons, GDSII files) and model types (prediction and correction). Gradient
|
|
7
|
+
computation is available for inverse design applications using automatic
|
|
8
|
+
differentiation.
|
|
9
9
|
"""
|
|
10
10
|
|
|
11
11
|
import base64
|
|
12
|
+
import gzip
|
|
12
13
|
import io
|
|
13
14
|
import json
|
|
14
15
|
import os
|
|
@@ -26,8 +27,17 @@ from PIL import Image
|
|
|
26
27
|
from .geometry import binarize_hard
|
|
27
28
|
from .models import Model
|
|
28
29
|
|
|
29
|
-
|
|
30
|
-
|
|
30
|
+
BASE_URL = "https://prefab-photonics"
|
|
31
|
+
|
|
32
|
+
# Endpoint versions
|
|
33
|
+
PREDICT_VERSION = "3"
|
|
34
|
+
PREDICT_POLY_VERSION = "3"
|
|
35
|
+
VJP_VERSION = "3"
|
|
36
|
+
|
|
37
|
+
# Endpoint URLs
|
|
38
|
+
PREDICT_ENDPOINT = f"{BASE_URL}--predict-v{PREDICT_VERSION}.modal.run"
|
|
39
|
+
PREDICT_POLY_ENDPOINT = f"{BASE_URL}--predict-poly-v{PREDICT_POLY_VERSION}.modal.run"
|
|
40
|
+
VJP_ENDPOINT = f"{BASE_URL}--vjp-v{VJP_VERSION}.modal.run"
|
|
31
41
|
|
|
32
42
|
|
|
33
43
|
def predict_array(
|
|
@@ -75,7 +85,7 @@ def predict_array(
|
|
|
75
85
|
ValueError
|
|
76
86
|
If the server returns an error or invalid response.
|
|
77
87
|
"""
|
|
78
|
-
endpoint_url =
|
|
88
|
+
endpoint_url = PREDICT_ENDPOINT
|
|
79
89
|
predict_data = {
|
|
80
90
|
"device_array": _encode_array(np.squeeze(device_array)),
|
|
81
91
|
"model": model.to_json(),
|
|
@@ -167,7 +177,7 @@ def _predict_poly(
|
|
|
167
177
|
"eta": eta,
|
|
168
178
|
}
|
|
169
179
|
|
|
170
|
-
endpoint_url =
|
|
180
|
+
endpoint_url = PREDICT_POLY_ENDPOINT
|
|
171
181
|
headers = _prepare_headers()
|
|
172
182
|
|
|
173
183
|
try:
|
|
@@ -364,47 +374,32 @@ def predict_gdstk(
|
|
|
364
374
|
return result_cell
|
|
365
375
|
|
|
366
376
|
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
) -> tuple[npt.NDArray[Any], npt.NDArray[Any]]:
|
|
370
|
-
"""
|
|
371
|
-
Predict the nanofabrication outcome of a device array and compute its gradient.
|
|
372
|
-
|
|
373
|
-
This function predicts the outcome of the nanofabrication process for a given
|
|
374
|
-
device array using a specified model. It also computes the gradient of the
|
|
375
|
-
prediction with respect to the input device array.
|
|
376
|
-
|
|
377
|
-
Parameters
|
|
378
|
-
----------
|
|
379
|
-
device_array : np.ndarray
|
|
380
|
-
A 2D array representing the planar geometry of the device.
|
|
381
|
-
model : Model
|
|
382
|
-
The model to use for prediction, representing a specific fabrication process.
|
|
377
|
+
# Storage for caching prediction results for VJP computation
|
|
378
|
+
_diff_cache: dict[int, tuple[npt.NDArray[Any], Model]] = {}
|
|
383
379
|
|
|
384
|
-
Returns
|
|
385
|
-
-------
|
|
386
|
-
tuple[np.ndarray, np.ndarray]
|
|
387
|
-
The predicted output array and gradient array.
|
|
388
380
|
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
"""
|
|
381
|
+
def _compute_vjp(
|
|
382
|
+
device_array: npt.NDArray[Any],
|
|
383
|
+
upstream_gradient: npt.NDArray[Any],
|
|
384
|
+
model: Model,
|
|
385
|
+
) -> npt.NDArray[Any]:
|
|
386
|
+
"""Compute J.T @ upstream_gradient via the server-side VJP endpoint."""
|
|
396
387
|
headers = _prepare_headers()
|
|
397
|
-
|
|
388
|
+
upstream_arr = np.squeeze(upstream_gradient).astype(np.float32)
|
|
389
|
+
vjp_data = {
|
|
398
390
|
"device_array": _encode_array(np.squeeze(device_array)),
|
|
391
|
+
"upstream_gradient": base64.b64encode(
|
|
392
|
+
gzip.compress(upstream_arr.tobytes(), compresslevel=1)
|
|
393
|
+
).decode("utf-8"),
|
|
394
|
+
"upstream_gradient_shape": list(upstream_arr.shape),
|
|
399
395
|
"model": model.to_json(),
|
|
400
396
|
"model_type": "p",
|
|
401
|
-
"binary": False,
|
|
402
397
|
}
|
|
403
|
-
endpoint_url =
|
|
398
|
+
endpoint_url = VJP_ENDPOINT
|
|
404
399
|
|
|
405
400
|
try:
|
|
406
401
|
response = requests.post(
|
|
407
|
-
endpoint_url, data=json.dumps(
|
|
402
|
+
endpoint_url, data=json.dumps(vjp_data), headers=headers
|
|
408
403
|
)
|
|
409
404
|
response.raise_for_status()
|
|
410
405
|
|
|
@@ -414,89 +409,152 @@ def _predict_array_with_grad(
|
|
|
414
409
|
response_data = response.json()
|
|
415
410
|
|
|
416
411
|
if "error" in response_data:
|
|
417
|
-
raise ValueError(f"
|
|
412
|
+
raise ValueError(f"VJP error: {response_data['error']}")
|
|
418
413
|
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
return (prediction_array, gradient_array)
|
|
414
|
+
vjp_array = _decode_array(response_data["vjp_array"])
|
|
415
|
+
vjp_min = response_data["vjp_min"]
|
|
416
|
+
vjp_max = response_data["vjp_max"]
|
|
417
|
+
vjp_range = vjp_max - vjp_min
|
|
418
|
+
vjp_array = vjp_array * vjp_range + vjp_min
|
|
419
|
+
return vjp_array
|
|
426
420
|
|
|
427
421
|
except requests.exceptions.RequestException as e:
|
|
428
|
-
raise RuntimeError(f"
|
|
422
|
+
raise RuntimeError(f"VJP request failed: {e}") from e
|
|
429
423
|
except json.JSONDecodeError as e:
|
|
430
424
|
raise ValueError(f"JSON decode error: {e}") from e
|
|
431
425
|
|
|
432
426
|
|
|
433
427
|
@primitive
|
|
434
|
-
def
|
|
428
|
+
def predict_array_diff(
|
|
435
429
|
device_array: npt.NDArray[Any], model: Model
|
|
436
430
|
) -> npt.NDArray[Any]:
|
|
437
431
|
"""
|
|
438
|
-
|
|
432
|
+
Differentiable fab prediction with exact gradient support.
|
|
439
433
|
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
prediction with respect to the input device array, making it suitable for use in
|
|
443
|
-
automatic differentiation applications (e.g., autograd).
|
|
434
|
+
Compatible with autograd for automatic differentiation. Gradients are computed via
|
|
435
|
+
a server-side VJP endpoint during the backward pass.
|
|
444
436
|
|
|
445
437
|
Parameters
|
|
446
438
|
----------
|
|
447
439
|
device_array : np.ndarray
|
|
448
440
|
A 2D array representing the planar geometry of the device.
|
|
449
441
|
model : Model
|
|
450
|
-
The model to use for prediction
|
|
442
|
+
The model to use for prediction.
|
|
451
443
|
|
|
452
444
|
Returns
|
|
453
445
|
-------
|
|
454
446
|
np.ndarray
|
|
455
|
-
The predicted
|
|
456
|
-
|
|
457
|
-
Raises
|
|
458
|
-
------
|
|
459
|
-
RuntimeError
|
|
460
|
-
If the request to the prediction service fails.
|
|
461
|
-
ValueError
|
|
462
|
-
If the server returns an error or invalid response.
|
|
447
|
+
The predicted fabrication outcome array.
|
|
463
448
|
"""
|
|
464
|
-
|
|
465
|
-
|
|
449
|
+
# Use standard forward pass
|
|
450
|
+
prediction_array = predict_array(
|
|
451
|
+
device_array=device_array,
|
|
452
|
+
model=model,
|
|
453
|
+
model_type="p",
|
|
454
|
+
binarize=False,
|
|
466
455
|
)
|
|
467
|
-
|
|
456
|
+
|
|
457
|
+
# Cache the input for VJP computation during backward pass
|
|
458
|
+
_diff_cache[id(prediction_array)] = (device_array.copy(), model)
|
|
459
|
+
|
|
468
460
|
return prediction_array
|
|
469
461
|
|
|
470
462
|
|
|
471
|
-
def
|
|
472
|
-
ans: npt.NDArray[Any], device_array: npt.NDArray[Any],
|
|
463
|
+
def _predict_array_diff_vjp(
|
|
464
|
+
ans: npt.NDArray[Any], device_array: npt.NDArray[Any], model: Model
|
|
473
465
|
) -> Any:
|
|
466
|
+
"""Define the exact VJP for predict_array_diff using server-side computation."""
|
|
467
|
+
cache_key = id(ans)
|
|
468
|
+
cached_device_array, cached_model = _diff_cache.get(
|
|
469
|
+
cache_key, (device_array, model)
|
|
470
|
+
)
|
|
471
|
+
|
|
472
|
+
def vjp(g: npt.NDArray[Any]) -> tuple[npt.NDArray[Any], None]:
|
|
473
|
+
# Compute exact VJP: J.T @ g via server endpoint
|
|
474
|
+
vjp_result = _compute_vjp(
|
|
475
|
+
device_array=cached_device_array,
|
|
476
|
+
upstream_gradient=g,
|
|
477
|
+
model=cached_model,
|
|
478
|
+
)
|
|
479
|
+
# Clean up cache
|
|
480
|
+
_diff_cache.pop(cache_key, None)
|
|
481
|
+
# Ensure gradient shape matches input shape
|
|
482
|
+
vjp_result = vjp_result.reshape(cached_device_array.shape)
|
|
483
|
+
# Return gradient for device_array, None for model (not differentiable)
|
|
484
|
+
return (vjp_result, None)
|
|
485
|
+
|
|
486
|
+
return vjp
|
|
487
|
+
|
|
488
|
+
|
|
489
|
+
defvjp(predict_array_diff, _predict_array_diff_vjp)
|
|
490
|
+
|
|
491
|
+
|
|
492
|
+
# Alias for backward compatibility with existing code
|
|
493
|
+
predict_array_with_grad = predict_array_diff
|
|
494
|
+
"""Alias for predict_array_diff. Deprecated, use predict_array_diff directly."""
|
|
495
|
+
|
|
496
|
+
|
|
497
|
+
def differentiable(model: Model):
|
|
474
498
|
"""
|
|
475
|
-
|
|
499
|
+
Create a model-bound differentiable predictor for clean autograd integration.
|
|
500
|
+
|
|
501
|
+
Returns a function that takes only `device_array` as input, enabling seamless
|
|
502
|
+
composition with other differentiable functions. The VJP returns a single
|
|
503
|
+
gradient array (not a tuple), making it compatible with standard autograd workflows.
|
|
476
504
|
|
|
477
505
|
Parameters
|
|
478
506
|
----------
|
|
479
|
-
|
|
480
|
-
The
|
|
481
|
-
device_array : np.ndarray
|
|
482
|
-
The input device array for which the gradient is computed.
|
|
483
|
-
*args :
|
|
484
|
-
Additional arguments that aren't used in the VJP computation.
|
|
507
|
+
model : Model
|
|
508
|
+
The model to use for prediction.
|
|
485
509
|
|
|
486
510
|
Returns
|
|
487
511
|
-------
|
|
488
|
-
|
|
489
|
-
A function that
|
|
512
|
+
callable
|
|
513
|
+
A differentiable prediction function that takes `device_array` and returns
|
|
514
|
+
the predicted fabrication outcome.
|
|
515
|
+
|
|
516
|
+
Examples
|
|
517
|
+
--------
|
|
518
|
+
>>> predictor = pf.predict.differentiable(model)
|
|
519
|
+
>>> def loss_fn(x):
|
|
520
|
+
... pred = predictor(x)
|
|
521
|
+
... return np.mean((pred - target) ** 2)
|
|
522
|
+
>>> gradient = grad(loss_fn)(device_array) # Returns array, not tuple
|
|
490
523
|
"""
|
|
491
|
-
grad_x = predict_array_with_grad.gradient_array # pyright: ignore[reportFunctionMemberAccess]
|
|
492
524
|
|
|
493
|
-
|
|
494
|
-
|
|
525
|
+
@primitive
|
|
526
|
+
def predict(device_array: npt.NDArray[Any]) -> npt.NDArray[Any]:
|
|
527
|
+
prediction = predict_array(
|
|
528
|
+
device_array=device_array,
|
|
529
|
+
model=model,
|
|
530
|
+
model_type="p",
|
|
531
|
+
binarize=False,
|
|
532
|
+
)
|
|
533
|
+
_diff_cache[id(prediction)] = (device_array.copy(), model)
|
|
534
|
+
return prediction
|
|
535
|
+
|
|
536
|
+
def predict_vjp(
|
|
537
|
+
ans: npt.NDArray[Any], device_array: npt.NDArray[Any]
|
|
538
|
+
) -> Any:
|
|
539
|
+
cache_key = id(ans)
|
|
540
|
+
cached_device_array, cached_model = _diff_cache.get(
|
|
541
|
+
cache_key, (device_array, model)
|
|
542
|
+
)
|
|
495
543
|
|
|
496
|
-
|
|
544
|
+
def vjp(g: npt.NDArray[Any]) -> npt.NDArray[Any]:
|
|
545
|
+
vjp_result = _compute_vjp(
|
|
546
|
+
device_array=cached_device_array,
|
|
547
|
+
upstream_gradient=g,
|
|
548
|
+
model=cached_model,
|
|
549
|
+
)
|
|
550
|
+
_diff_cache.pop(cache_key, None)
|
|
551
|
+
# Ensure gradient shape matches input shape
|
|
552
|
+
return vjp_result.reshape(device_array.shape)
|
|
497
553
|
|
|
554
|
+
return vjp
|
|
498
555
|
|
|
499
|
-
defvjp(
|
|
556
|
+
defvjp(predict, predict_vjp)
|
|
557
|
+
return predict
|
|
500
558
|
|
|
501
559
|
|
|
502
560
|
def _encode_array(array: npt.NDArray[Any]) -> str:
|
|
@@ -512,7 +570,7 @@ def _decode_array(encoded_png: str) -> npt.NDArray[Any]:
|
|
|
512
570
|
"""Decode a base64 encoded image and return an ndarray."""
|
|
513
571
|
binary_data = base64.b64decode(encoded_png)
|
|
514
572
|
image = Image.open(io.BytesIO(binary_data))
|
|
515
|
-
return np.array(image) / 255
|
|
573
|
+
return np.array(image) / 255
|
|
516
574
|
|
|
517
575
|
|
|
518
576
|
def _prepare_headers() -> dict[str, str]:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: prefab
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.6.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
|
|
@@ -511,14 +511,13 @@ License: GNU LESSER GENERAL PUBLIC LICENSE
|
|
|
511
511
|
|
|
512
512
|
That's all there is to it!
|
|
513
513
|
License-File: LICENSE
|
|
514
|
-
Keywords:
|
|
514
|
+
Keywords: electronic-design-automation,integrated-photonics,inverse-design,lithography,nanofabrication,photonics,semiconductor-manufacturing,silicon-photonics
|
|
515
515
|
Classifier: License :: OSI Approved :: GNU Lesser General Public License v2 (LGPLv2)
|
|
516
516
|
Classifier: Operating System :: OS Independent
|
|
517
517
|
Classifier: Programming Language :: Python :: 3
|
|
518
518
|
Requires-Python: >=3.9
|
|
519
519
|
Requires-Dist: autograd
|
|
520
520
|
Requires-Dist: gdstk>=0.9.55
|
|
521
|
-
Requires-Dist: ipykernel>=6.31.0
|
|
522
521
|
Requires-Dist: matplotlib
|
|
523
522
|
Requires-Dist: numpy
|
|
524
523
|
Requires-Dist: opencv-python-headless
|
|
@@ -528,9 +527,6 @@ Requires-Dist: requests
|
|
|
528
527
|
Requires-Dist: scikit-image
|
|
529
528
|
Requires-Dist: scipy
|
|
530
529
|
Requires-Dist: toml
|
|
531
|
-
Provides-Extra: dev
|
|
532
|
-
Requires-Dist: jupyter; extra == 'dev'
|
|
533
|
-
Requires-Dist: jupyterlab; extra == 'dev'
|
|
534
530
|
Provides-Extra: docs
|
|
535
531
|
Requires-Dist: mkdocs; extra == 'docs'
|
|
536
532
|
Requires-Dist: mkdocs-material; extra == 'docs'
|
|
@@ -1,17 +1,17 @@
|
|
|
1
|
-
prefab/__init__.py,sha256=
|
|
2
|
-
prefab/__main__.py,sha256=
|
|
1
|
+
prefab/__init__.py,sha256=WmddNmU7gmyH4cqkT6BW4iQ3RDG_l5SpKxg4poS6wsU,425
|
|
2
|
+
prefab/__main__.py,sha256=uL6AdCeimPbXiWv0gnq9TDeZhAIrxGwJsEKNYbg9MZg,3454
|
|
3
3
|
prefab/compare.py,sha256=aX7nr9tznSebYeeztvqIPz57npnJ4-iUeKEedrZdksE,3676
|
|
4
4
|
prefab/device.py,sha256=1O6vTOq4wQRGVYvFWLH0uj1XhhYCfnDnIapDEYnBKHw,47996
|
|
5
5
|
prefab/geometry.py,sha256=-nTaGjdw3KN1SVoyvqdcrE2GJP7OqPF6ivUhrO78rUk,11244
|
|
6
|
-
prefab/predict.py,sha256=
|
|
6
|
+
prefab/predict.py,sha256=gxxHCcaMv2TE5O639mQzO3IzFRsLGjY-FQowVp5vLkI,20659
|
|
7
7
|
prefab/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
8
8
|
prefab/read.py,sha256=5BmvFemobA72urxs4j2VZRVvanZZGu1mDB1Uh-epyvI,8635
|
|
9
9
|
prefab/shapes.py,sha256=mRGwsPS-A9XsW3jgvUuMCEeNv9BLXsEnJkytlIHUAKE,28802
|
|
10
10
|
prefab/models/__init__.py,sha256=rRrjcOcHPcob98Coksc0tbvYcXbm6SoLEb-Md233Jvo,1391
|
|
11
11
|
prefab/models/base.py,sha256=t4VNMsOztPedj3kN5fZ1-4tk0SRHWrMuqnIVHztsCs4,1514
|
|
12
12
|
prefab/models/evaluation.py,sha256=2_Klui6tY8xPvOSVD8VpZCVAnT1RX15FONqWG-_x-J8,484
|
|
13
|
-
prefab-1.
|
|
14
|
-
prefab-1.
|
|
15
|
-
prefab-1.
|
|
16
|
-
prefab-1.
|
|
17
|
-
prefab-1.
|
|
13
|
+
prefab-1.6.0.dist-info/METADATA,sha256=CvuP1Khblh_LA-vx5NLgTH5A_zaewTkklkaPEsIY4hI,33754
|
|
14
|
+
prefab-1.6.0.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
15
|
+
prefab-1.6.0.dist-info/entry_points.txt,sha256=h1_A9O9F3NAIoKXD1RPb3Eo-WCSiHhMB_AnagBi6XTQ,48
|
|
16
|
+
prefab-1.6.0.dist-info/licenses/LICENSE,sha256=IMF9i4xIpgCADf0U-V1cuf9HBmqWQd3qtI3FSuyW4zE,26526
|
|
17
|
+
prefab-1.6.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|