prefab 1.4.1__py3-none-any.whl → 1.5.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 CHANGED
@@ -5,7 +5,7 @@ Usage:
5
5
  import prefab as pf
6
6
  """
7
7
 
8
- __version__ = "1.4.1"
8
+ __version__ = "1.5.0"
9
9
 
10
10
  from . import compare, geometry, predict, read, shapes
11
11
  from .device import BufferSpec, Device
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/examples to start.\n"
22
- "Reach out to us at hi@prefabphotonics.com for support."
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,9 +3,9 @@ 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, correction,
7
- segmentation). Gradient computation is available for inverse design applications
8
- using automatic differentiation.
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
@@ -26,8 +26,17 @@ from PIL import Image
26
26
  from .geometry import binarize_hard
27
27
  from .models import Model
28
28
 
29
- BASE_ENDPOINT_URL = "https://prefab-photonics--predict"
30
- ENDPOINT_VERSION = "3"
29
+ BASE_URL = "https://prefab-photonics"
30
+
31
+ # Endpoint versions
32
+ PREDICT_VERSION = "3"
33
+ PREDICT_POLY_VERSION = "3"
34
+ VJP_VERSION = "3"
35
+
36
+ # Endpoint URLs
37
+ PREDICT_ENDPOINT = f"{BASE_URL}--predict-v{PREDICT_VERSION}.modal.run"
38
+ PREDICT_POLY_ENDPOINT = f"{BASE_URL}--predict-poly-v{PREDICT_POLY_VERSION}.modal.run"
39
+ VJP_ENDPOINT = f"{BASE_URL}--vjp-v{VJP_VERSION}.modal.run"
31
40
 
32
41
 
33
42
  def predict_array(
@@ -75,7 +84,7 @@ def predict_array(
75
84
  ValueError
76
85
  If the server returns an error or invalid response.
77
86
  """
78
- endpoint_url = f"{BASE_ENDPOINT_URL}-v{ENDPOINT_VERSION}.modal.run"
87
+ endpoint_url = PREDICT_ENDPOINT
79
88
  predict_data = {
80
89
  "device_array": _encode_array(np.squeeze(device_array)),
81
90
  "model": model.to_json(),
@@ -167,7 +176,7 @@ def _predict_poly(
167
176
  "eta": eta,
168
177
  }
169
178
 
170
- endpoint_url = f"{BASE_ENDPOINT_URL}-poly-v{ENDPOINT_VERSION}.modal.run"
179
+ endpoint_url = PREDICT_POLY_ENDPOINT
171
180
  headers = _prepare_headers()
172
181
 
173
182
  try:
@@ -364,47 +373,28 @@ def predict_gdstk(
364
373
  return result_cell
365
374
 
366
375
 
367
- def _predict_array_with_grad(
368
- device_array: npt.NDArray[Any], model: Model
369
- ) -> tuple[npt.NDArray[Any], npt.NDArray[Any]]:
370
- """
371
- Predict the nanofabrication outcome of a device array and compute its gradient.
376
+ # Storage for caching prediction results for VJP computation
377
+ _diff_cache: dict[int, tuple[npt.NDArray[Any], Model]] = {}
372
378
 
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.
383
-
384
- Returns
385
- -------
386
- tuple[np.ndarray, np.ndarray]
387
- The predicted output array and gradient array.
388
379
 
389
- Raises
390
- ------
391
- RuntimeError
392
- If the request to the prediction service fails.
393
- ValueError
394
- If the server returns an error or invalid response.
395
- """
380
+ def _compute_vjp(
381
+ device_array: npt.NDArray[Any],
382
+ upstream_gradient: npt.NDArray[Any],
383
+ model: Model,
384
+ ) -> npt.NDArray[Any]:
385
+ """Compute J.T @ upstream_gradient via the server-side VJP endpoint."""
396
386
  headers = _prepare_headers()
397
- predict_data = {
387
+ vjp_data = {
398
388
  "device_array": _encode_array(np.squeeze(device_array)),
389
+ "upstream_gradient": _encode_array(np.squeeze(upstream_gradient)),
399
390
  "model": model.to_json(),
400
391
  "model_type": "p",
401
- "binary": False,
402
392
  }
403
- endpoint_url = f"{BASE_ENDPOINT_URL}-with-grad-v{ENDPOINT_VERSION}.modal.run"
393
+ endpoint_url = VJP_ENDPOINT
404
394
 
405
395
  try:
406
396
  response = requests.post(
407
- endpoint_url, data=json.dumps(predict_data), headers=headers
397
+ endpoint_url, data=json.dumps(vjp_data), headers=headers
408
398
  )
409
399
  response.raise_for_status()
410
400
 
@@ -414,89 +404,87 @@ def _predict_array_with_grad(
414
404
  response_data = response.json()
415
405
 
416
406
  if "error" in response_data:
417
- raise ValueError(f"Prediction error: {response_data['error']}")
407
+ raise ValueError(f"VJP error: {response_data['error']}")
418
408
 
419
- prediction_array = _decode_array(response_data["prediction_array"])
420
- gradient_array = _decode_array(response_data["gradient_array"])
421
- gradient_min = response_data["gradient_min"]
422
- gradient_max = response_data["gradient_max"]
423
- gradient_range = gradient_max - gradient_min
424
- gradient_array = gradient_array * gradient_range + gradient_min
425
- return (prediction_array, gradient_array)
409
+ vjp_array = _decode_array(response_data["vjp_array"])
410
+ vjp_min = response_data["vjp_min"]
411
+ vjp_max = response_data["vjp_max"]
412
+ vjp_range = vjp_max - vjp_min
413
+ vjp_array = vjp_array * vjp_range + vjp_min
414
+ return vjp_array
426
415
 
427
416
  except requests.exceptions.RequestException as e:
428
- raise RuntimeError(f"Request failed: {e}") from e
417
+ raise RuntimeError(f"VJP request failed: {e}") from e
429
418
  except json.JSONDecodeError as e:
430
419
  raise ValueError(f"JSON decode error: {e}") from e
431
420
 
432
421
 
433
422
  @primitive
434
- def predict_array_with_grad(
423
+ def predict_array_diff(
435
424
  device_array: npt.NDArray[Any], model: Model
436
425
  ) -> npt.NDArray[Any]:
437
426
  """
438
- Predict the nanofabrication outcome of a device array and compute its gradient.
427
+ Differentiable fab prediction with exact gradient support.
439
428
 
440
- This function predicts the outcome of the nanofabrication process for a given
441
- device array using a specified model. It also computes the gradient of the
442
- prediction with respect to the input device array, making it suitable for use in
443
- automatic differentiation applications (e.g., autograd).
429
+ Compatible with autograd for automatic differentiation. Gradients are computed via
430
+ a server-side VJP endpoint during the backward pass.
444
431
 
445
432
  Parameters
446
433
  ----------
447
434
  device_array : np.ndarray
448
435
  A 2D array representing the planar geometry of the device.
449
436
  model : Model
450
- The model to use for prediction, representing a specific fabrication process.
437
+ The model to use for prediction.
451
438
 
452
439
  Returns
453
440
  -------
454
441
  np.ndarray
455
- The predicted output array.
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.
442
+ The predicted fabrication outcome array.
463
443
  """
464
- prediction_array, gradient_array = _predict_array_with_grad(
465
- device_array=device_array, model=model
444
+ # Use standard forward pass
445
+ prediction_array = predict_array(
446
+ device_array=device_array,
447
+ model=model,
448
+ model_type="p",
449
+ binarize=False,
466
450
  )
467
- predict_array_with_grad.gradient_array = gradient_array # pyright: ignore[reportFunctionMemberAccess]
451
+
452
+ # Cache the input for VJP computation during backward pass
453
+ _diff_cache[id(prediction_array)] = (device_array.copy(), model)
454
+
468
455
  return prediction_array
469
456
 
470
457
 
471
- def predict_array_with_grad_vjp(
472
- ans: npt.NDArray[Any], device_array: npt.NDArray[Any], *args: Any
458
+ def _predict_array_diff_vjp(
459
+ ans: npt.NDArray[Any], device_array: npt.NDArray[Any], model: Model
473
460
  ) -> Any:
474
- """
475
- Define the vector-Jacobian product (VJP) for the prediction function.
461
+ """Define the exact VJP for predict_array_diff using server-side computation."""
462
+ cache_key = id(ans)
463
+ cached_device_array, cached_model = _diff_cache.get(
464
+ cache_key, (device_array, model)
465
+ )
476
466
 
477
- Parameters
478
- ----------
479
- ans : np.ndarray
480
- The output of the `predict_array_with_grad` function.
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.
467
+ def vjp(g: npt.NDArray[Any]) -> tuple[npt.NDArray[Any], None]:
468
+ # Compute exact VJP: J.T @ g via server endpoint
469
+ vjp_result = _compute_vjp(
470
+ device_array=cached_device_array,
471
+ upstream_gradient=g,
472
+ model=cached_model,
473
+ )
474
+ # Clean up cache
475
+ _diff_cache.pop(cache_key, None)
476
+ # Return gradient for device_array, None for model (not differentiable)
477
+ return (vjp_result, None)
485
478
 
486
- Returns
487
- -------
488
- function
489
- A function that computes the VJP given an upstream gradient `g`.
490
- """
491
- grad_x = predict_array_with_grad.gradient_array # pyright: ignore[reportFunctionMemberAccess]
479
+ return vjp
492
480
 
493
- def vjp(g: npt.NDArray[Any]) -> npt.NDArray[Any]:
494
- return g * grad_x # type: ignore[no-any-return]
495
481
 
496
- return vjp
482
+ defvjp(predict_array_diff, _predict_array_diff_vjp)
497
483
 
498
484
 
499
- defvjp(predict_array_with_grad, predict_array_with_grad_vjp)
485
+ # Alias for backward compatibility with existing code
486
+ predict_array_with_grad = predict_array_diff
487
+ """Alias for predict_array_diff. Deprecated, use predict_array_diff directly."""
500
488
 
501
489
 
502
490
  def _encode_array(array: npt.NDArray[Any]) -> str:
@@ -517,6 +505,12 @@ def _decode_array(encoded_png: str) -> npt.NDArray[Any]:
517
505
 
518
506
  def _prepare_headers() -> dict[str, str]:
519
507
  """Prepare HTTP headers for a server request."""
508
+ # Check for API key first (for headless/server environments)
509
+ api_key = os.environ.get("PREFAB_API_KEY")
510
+ if api_key:
511
+ return {"X-API-Key": api_key}
512
+
513
+ # Fall back to token file (browser OAuth flow)
520
514
  token_file_path = os.path.expanduser("~/.prefab.toml")
521
515
  try:
522
516
  with open(token_file_path) as file:
@@ -532,7 +526,6 @@ def _prepare_headers() -> dict[str, str]:
532
526
  except FileNotFoundError:
533
527
  raise FileNotFoundError(
534
528
  "Could not validate user.\n"
535
- + "Please update prefab using: pip install --upgrade prefab.\n"
536
- + "Signup/login and generate a new token.\n"
529
+ + "Set PREFAB_API_KEY environment variable, or run 'prefab setup'.\n"
537
530
  + "See https://docs.prefabphotonics.com/."
538
531
  ) from None
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: prefab
3
- Version: 1.4.1
3
+ Version: 1.5.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: computer-vision,deep-learning,electronic-design-automation,integrated-photonics,machine-learning,nanofabrication,semiconductor-manufacturing
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=f9FN73XE4ufrKEDyy5rGQW2sWWH-8Y4C_7FYwO946Rs,425
2
- prefab/__main__.py,sha256=M5ulRfgSMdPjeHPoMGQ3P86zFMJDII5I18NGrpFUcsM,3458
1
+ prefab/__init__.py,sha256=shfnU_-iDY4MavJaXVb-Xzr9Y7w2_9zhShi4INgEPg4,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=066X_SnSIHPqaPMbk0Yn_yX_KBfUc9yxrsmP_k56FU0,19015
6
+ prefab/predict.py,sha256=xEpzyS8uRMZFohBlTGmL90LmVsIn_41ZN7RRhwsw7Ww,18413
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.4.1.dist-info/METADATA,sha256=miTQTlHxSgZn57wzltpGpAZ4PgUBW-hVSWmxmSDGqCA,33880
14
- prefab-1.4.1.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
15
- prefab-1.4.1.dist-info/entry_points.txt,sha256=h1_A9O9F3NAIoKXD1RPb3Eo-WCSiHhMB_AnagBi6XTQ,48
16
- prefab-1.4.1.dist-info/licenses/LICENSE,sha256=IMF9i4xIpgCADf0U-V1cuf9HBmqWQd3qtI3FSuyW4zE,26526
17
- prefab-1.4.1.dist-info/RECORD,,
13
+ prefab-1.5.0.dist-info/METADATA,sha256=f8778_o8nPf4DIewUWqsK_2VfImxJish5I390Y9mtww,33754
14
+ prefab-1.5.0.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
15
+ prefab-1.5.0.dist-info/entry_points.txt,sha256=h1_A9O9F3NAIoKXD1RPb3Eo-WCSiHhMB_AnagBi6XTQ,48
16
+ prefab-1.5.0.dist-info/licenses/LICENSE,sha256=IMF9i4xIpgCADf0U-V1cuf9HBmqWQd3qtI3FSuyW4zE,26526
17
+ prefab-1.5.0.dist-info/RECORD,,
File without changes