get things working with a LazyLibrary hack while we think about cycles

This commit is contained in:
Jan Petykiewicz 2023-01-24 23:52:32 -08:00
parent 22735125d5
commit 6eb4af3203
5 changed files with 32 additions and 12 deletions

View File

@ -142,7 +142,7 @@ def waveguide(
left=Port((-extent, 0), rotation=0, ptype='pcwg'), left=Port((-extent, 0), rotation=0, ptype='pcwg'),
right=Port((extent, 0), rotation=pi, ptype='pcwg'), right=Port((extent, 0), rotation=pi, ptype='pcwg'),
) )
pat2dev(pat) dev2pat(pat)
print(pat) print(pat)
return pat return pat
@ -183,7 +183,7 @@ def bend(
extent * numpy.sqrt(3) / 2), extent * numpy.sqrt(3) / 2),
rotation=pi * 4 / 3, ptype='pcwg'), rotation=pi * 4 / 3, ptype='pcwg'),
) )
pat2dev(pat) dev2pat(pat)
return pat return pat
@ -222,7 +222,7 @@ def y_splitter(
'bot': Port((extent / 2, -extent * numpy.sqrt(3) / 2), rotation=pi * 2 / 3, ptype='pcwg'), 'bot': Port((extent / 2, -extent * numpy.sqrt(3) / 2), rotation=pi * 2 / 3, ptype='pcwg'),
} }
pat2dev(pat) dev2pat(pat)
return pat return pat
@ -311,7 +311,7 @@ def main(interactive: bool = True) -> None:
# We can also add text labels for our circuit's ports. # We can also add text labels for our circuit's ports.
# They will appear at the uppermost hierarchy level, while the individual # They will appear at the uppermost hierarchy level, while the individual
# device ports will appear further down, in their respective cells. # device ports will appear further down, in their respective cells.
pat2dev(circ.pattern) dev2pat(circ.pattern)
# Add the pattern into our library # Add the pattern into our library
lib['my_circuit'] = circ.pattern lib['my_circuit'] = circ.pattern
@ -319,7 +319,7 @@ def main(interactive: bool = True) -> None:
# Check if we forgot to include any patterns... ooops! # Check if we forgot to include any patterns... ooops!
if dangling := lib.dangling_refs(): if dangling := lib.dangling_refs():
print('Warning: The following patterns are referenced, but not present in the' print('Warning: The following patterns are referenced, but not present in the'
f'library! {dangling}') f' library! {dangling}')
print('We\'ll solve this by merging in shape_lib, which contains those shapes...') print('We\'ll solve this by merging in shape_lib, which contains those shapes...')
lib.add(shape_lib) lib.add(shape_lib)

View File

@ -1,9 +1,12 @@
from typing import Dict from typing import Dict, TypeVar
#from typing import Union, Optional, MutableMapping, TYPE_CHECKING #from typing import Union, Optional, MutableMapping, TYPE_CHECKING
import copy import copy
import logging import logging
from numpy.typing import ArrayLike
#from .pattern import Pattern #from .pattern import Pattern
from .ref import Ref
from .ports import PortList, Port from .ports import PortList, Port
#if TYPE_CHECKING: #if TYPE_CHECKING:

View File

@ -207,7 +207,7 @@ class Builder(PortList):
names. names.
""" """
if library is None: if library is None:
if hasattr(source, 'library') and isinstance(source, MutableLibrary): if hasattr(source, 'library') and isinstance(source.library, MutableLibrary):
library = source.library library = source.library
else: else:
raise BuildError('No library provided (and not present in `source.library`') raise BuildError('No library provided (and not present in `source.library`')

View File

@ -53,7 +53,9 @@ def dev2pat(pattern: Pattern, layer: layer_t) -> Pattern:
def pat2dev( def pat2dev(
layers: Sequence[layer_t], layers: Sequence[layer_t],
library: Mapping[str, Pattern], library: Mapping[str, Pattern],
pattern: Pattern, pattern: Pattern, # Pattern is good since we don't want to do library[name] to avoid infinite recursion.
# LazyLibrary protects against library[ref.target] causing a circular lookup.
# For others, maybe check for cycles up front? TODO
name: Optional[str] = None, name: Optional[str] = None,
max_depth: int = 999_999, max_depth: int = 999_999,
skip_subcells: bool = True, skip_subcells: bool = True,
@ -94,20 +96,24 @@ def pat2dev(
if (skip_subcells and pattern.ports) or max_depth == 0: if (skip_subcells and pattern.ports) or max_depth == 0:
return pattern return pattern
# Load ports for all subpatterns # Load ports for all subpatterns, and use any we find
for target in set(rr.target for rr in pat.refs): found_ports = False
for target in set(rr.target for rr in pattern.refs):
pp = pat2dev( pp = pat2dev(
layers=layers, layers=layers,
library=library, library=library,
pattern=library[target], pattern=library[target],
name=target, name=target,
max_depth=max_depth-1, max_depth=max_depth - 1,
skip_subcells=skip_subcells, skip_subcells=skip_subcells,
blacklist=blacklist + {name}, blacklist=blacklist + {name},
) )
found_ports |= bool(pp.ports) found_ports |= bool(pp.ports)
for ref in pat.refs: if not found_ports:
return pattern
for ref in pattern.refs:
aa = library.abstract(ref.target) aa = library.abstract(ref.target)
if not aa.ports: if not aa.ports:
continue continue

View File

@ -713,11 +713,13 @@ class LazyLibrary(MutableLibrary):
""" """
dict: Dict[str, Callable[[], 'Pattern']] dict: Dict[str, Callable[[], 'Pattern']]
cache: Dict[str, 'Pattern'] cache: Dict[str, 'Pattern']
_lookups_in_progress: Set[str]
enable_cache: bool = True enable_cache: bool = True
def __init__(self) -> None: def __init__(self) -> None:
self.dict = {} self.dict = {}
self.cache = {} self.cache = {}
self._lookups_in_progress = set()
def __setitem__(self, key: str, value: Callable[[], 'Pattern']) -> None: def __setitem__(self, key: str, value: Callable[[], 'Pattern']) -> None:
self.dict[key] = value self.dict[key] = value
@ -735,8 +737,17 @@ class LazyLibrary(MutableLibrary):
logger.debug(f'found {key} in cache') logger.debug(f'found {key} in cache')
return self.cache[key] return self.cache[key]
if key in self._lookups_in_progress:
raise LibraryError(
f'Detected multiple simultaneous lookups of "{key}".\n'
'This may be caused by an invalid (cyclical) reference, or buggy code.\n'
'If you are lazy-loading a file, try a non-lazy load and check for refernce cycles.' # TODO give advice on finding cycles
)
self._lookups_in_progress.add(key)
func = self.dict[key] func = self.dict[key]
pat = func() pat = func()
self._lookups_in_progress.remove(key)
self.cache[key] = pat self.cache[key] = pat
return pat return pat