Compare commits

..

No commits in common. "f28c31fe2917f480584a9ebb921b7690cf63acdc" and "b4d31903c10fe8bea48a92ad3f344b76c7ef9e80" have entirely different histories.

4 changed files with 16 additions and 77 deletions

View File

@ -101,7 +101,7 @@ References are accomplished by listing the target's name, not its `Pattern` obje
## Glossary ## Glossary
- `Library`: A collection of named cells. OASIS or GDS "library" or file. - `Library`: A collection of named cells. OASIS or GDS "library" or file.
- `Tree`: Any `{name: pattern}` mapping which has only one topcell. - "tree": Any Library which has only one topcell.
- `Pattern`: A collection of geometry, text labels, and reference to other patterns. - `Pattern`: A collection of geometry, text labels, and reference to other patterns.
OASIS or GDS "Cell", DXF "Block". OASIS or GDS "Cell", DXF "Block".
- `Ref`: A reference to another pattern. GDS "AREF/SREF", OASIS "Placement". - `Ref`: A reference to another pattern. GDS "AREF/SREF", OASIS "Placement".
@ -142,11 +142,6 @@ my_pattern.ref(new_name, ...) # instantiate the cell
# In practice, you may do lots of # In practice, you may do lots of
my_pattern.ref(lib << make_tree(...), ...) my_pattern.ref(lib << make_tree(...), ...)
# With a `Builder` and `place()`/`plug()` the `lib <<` portion can be implicit:
my_builder = Builder(library=lib, ...)
...
my_builder.place(make_tree(...))
``` ```
We can also use this shorthand to quickly add and reference a single flat (as yet un-named) pattern: We can also use this shorthand to quickly add and reference a single flat (as yet un-named) pattern:

View File

@ -38,7 +38,7 @@ from .pattern import Pattern, map_layers, map_targets, chain_elements
from .library import ( from .library import (
ILibraryView, ILibrary, ILibraryView, ILibrary,
LibraryView, Library, LazyLibrary, LibraryView, Library, LazyLibrary,
AbstractView, TreeView, Tree, AbstractView,
) )
from .ports import Port, PortList from .ports import Port, PortList
from .abstract import Abstract from .abstract import Abstract

View File

@ -8,7 +8,7 @@ import logging
from numpy.typing import ArrayLike from numpy.typing import ArrayLike
from ..pattern import Pattern from ..pattern import Pattern
from ..library import ILibrary, TreeView from ..library import ILibrary
from ..error import BuildError from ..error import BuildError
from ..ports import PortList, Port from ..ports import PortList, Port
from ..abstract import Abstract from ..abstract import Abstract
@ -190,7 +190,7 @@ class Builder(PortList):
def plug( def plug(
self, self,
other: Abstract | str | Pattern | TreeView, other: Abstract | str | Pattern,
map_in: dict[str, str], map_in: dict[str, str],
map_out: dict[str, str | None] | None = None, map_out: dict[str, str | None] | None = None,
*, *,
@ -201,16 +201,11 @@ class Builder(PortList):
) -> Self: ) -> Self:
""" """
Wrapper around `Pattern.plug` which allows a string for `other`. Wrapper around `Pattern.plug` which allows a string for `other`.
The `Builder`'s library is used to dereference the string (or `Abstract`, if The `Builder`'s library is used to dereference the string (or `Abstract`, if
one is passed with `append=True`). If a `TreeView` is passed, it is first one is passed with `append=True`).
added into `self.library`.
Args: Args:
other: An `Abstract`, string, `Pattern`, or `TreeView` describing the other: An `Abstract`, string, or `Pattern` describing the device to be instatiated.
device to be instatiated. If it is a `TreeView`, it is first
added into `self.library`, after which the topcell is plugged;
an equivalent statement is `self.plug(self.library << other, ...)`.
map_in: dict of `{'self_port': 'other_port'}` mappings, specifying map_in: dict of `{'self_port': 'other_port'}` mappings, specifying
port connections between the two devices. port connections between the two devices.
map_out: dict of `{'old_name': 'new_name'}` mappings, specifying map_out: dict of `{'old_name': 'new_name'}` mappings, specifying
@ -248,10 +243,6 @@ class Builder(PortList):
logger.error('Skipping plug() since device is dead') logger.error('Skipping plug() since device is dead')
return self return self
if not isinstance(other, (str, Abstract, Pattern)):
# We got a Tree; add it into self.library and grab an Abstract for it
other = self.library << other
if isinstance(other, str): if isinstance(other, str):
other = self.library.abstract(other) other = self.library.abstract(other)
if append and isinstance(other, Abstract): if append and isinstance(other, Abstract):
@ -270,7 +261,7 @@ class Builder(PortList):
def place( def place(
self, self,
other: Abstract | str | Pattern | TreeView, other: Abstract | str | Pattern,
*, *,
offset: ArrayLike = (0, 0), offset: ArrayLike = (0, 0),
rotation: float = 0, rotation: float = 0,
@ -281,17 +272,12 @@ class Builder(PortList):
append: bool = False, append: bool = False,
) -> Self: ) -> Self:
""" """
Wrapper around `Pattern.place` which allows a string or `TreeView` for `other`. Wrapper around `Pattern.place` which allows a string for `other`.
The `Builder`'s library is used to dereference the string (or `Abstract`, if The `Builder`'s library is used to dereference the string (or `Abstract`, if
one is passed with `append=True`). If a `TreeView` is passed, it is first one is passed with `append=True`).
added into `self.library`.
Args: Args:
other: An `Abstract`, string, `Pattern`, or `TreeView` describing the other: An `Abstract`, string, or `Pattern` describing the device to be instatiated.
device to be instatiated. If it is a `TreeView`, it is first
added into `self.library`, after which the topcell is plugged;
an equivalent statement is `self.plug(self.library << other, ...)`.
offset: Offset at which to place the instance. Default (0, 0). offset: Offset at which to place the instance. Default (0, 0).
rotation: Rotation applied to the instance before placement. Default 0. rotation: Rotation applied to the instance before placement. Default 0.
pivot: Rotation is applied around this pivot point (default (0, 0)). pivot: Rotation is applied around this pivot point (default (0, 0)).
@ -320,10 +306,6 @@ class Builder(PortList):
logger.error('Skipping place() since device is dead') logger.error('Skipping place() since device is dead')
return self return self
if not isinstance(other, (str, Abstract, Pattern)):
# We got a Tree; add it into self.library and grab an Abstract for it
other = self.library << other
if isinstance(other, str): if isinstance(other, str):
other = self.library.abstract(other) other = self.library.abstract(other)
if append and isinstance(other, Abstract): if append and isinstance(other, Abstract):

View File

@ -14,7 +14,7 @@ Classes include:
- `AbstractView`: Provides a way to use []-indexing to generate abstracts for patterns in the linked - `AbstractView`: Provides a way to use []-indexing to generate abstracts for patterns in the linked
library. Generated with `ILibraryView.abstract_view()`. library. Generated with `ILibraryView.abstract_view()`.
""" """
from typing import Callable, Self, Type, TYPE_CHECKING, cast, TypeAlias, Protocol, Literal from typing import Callable, Self, Type, TYPE_CHECKING, cast
from typing import Iterator, Mapping, MutableMapping, Sequence from typing import Iterator, Mapping, MutableMapping, Sequence
import logging import logging
import base64 import base64
@ -26,7 +26,7 @@ from collections import defaultdict
from abc import ABCMeta, abstractmethod from abc import ABCMeta, abstractmethod
import numpy import numpy
from numpy.typing import ArrayLike, NDArray from numpy.typing import ArrayLike
from .error import LibraryError, PatternError from .error import LibraryError, PatternError
from .utils import rotation_matrix_2d, layer_t from .utils import rotation_matrix_2d, layer_t
@ -42,24 +42,7 @@ if TYPE_CHECKING:
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
class visitor_function_t(Protocol): visitor_function_t = Callable[..., 'Pattern']
""" Signature for `Library.dfs()` visitor functions. """
def __call__(
self,
pattern: 'Pattern',
hierarchy: tuple[str | None, ...],
memo: dict,
transform: NDArray[numpy.float64] | Literal[False],
) -> Pattern:
...
TreeView: TypeAlias = Mapping[str, 'Pattern']
""" A name-to-`Pattern` mapping which is expected to have only one top-level cell """
Tree: TypeAlias = MutableMapping[str, 'Pattern']
""" A mutable name-to-`Pattern` mapping which is expected to have only one top-level cell """
SINGLE_USE_PREFIX = '_' SINGLE_USE_PREFIX = '_'
""" """
@ -387,9 +370,6 @@ class ILibraryView(Mapping[str, 'Pattern'], metaclass=ABCMeta):
def top(self) -> str: def top(self) -> str:
""" """
Return the name of the topcell, or raise an exception if there isn't a single topcell Return the name of the topcell, or raise an exception if there isn't a single topcell
Raises:
LibraryError if there is not exactly one topcell.
""" """
tops = self.tops() tops = self.tops()
if len(tops) != 1: if len(tops) != 1:
@ -399,9 +379,6 @@ class ILibraryView(Mapping[str, 'Pattern'], metaclass=ABCMeta):
def top_pattern(self) -> 'Pattern': def top_pattern(self) -> 'Pattern':
""" """
Shorthand for self[self.top()] Shorthand for self[self.top()]
Raises:
LibraryError if there is not exactly one topcell.
""" """
return self[self.top()] return self[self.top()]
@ -461,7 +438,7 @@ class ILibraryView(Mapping[str, 'Pattern'], metaclass=ABCMeta):
if transform is None or transform is True: if transform is None or transform is True:
transform = numpy.zeros(4) transform = numpy.zeros(4)
elif transform is not False: elif transform is not False:
transform = numpy.array(transform, dtype=float, copy=False) transform = numpy.array(transform)
original_pattern = pattern original_pattern = pattern
@ -697,15 +674,7 @@ class ILibrary(ILibraryView, MutableMapping[str, 'Pattern'], metaclass=ABCMeta):
return rename_map return rename_map
def __lshift__(self, other: TreeView) -> str: def __lshift__(self, other: Mapping[str, 'Pattern']) -> str:
"""
`add()` items from a tree (single-topcell name: pattern mapping) into this one,
and return the name of the tree's topcell (in this library; it may have changed
based on `add()`'s default `rename_theirs` argument).
Raises:
LibraryError if there is more than one topcell in `other`.
"""
if len(other) == 1: if len(other) == 1:
name = next(iter(other)) name = next(iter(other))
else: else:
@ -723,13 +692,6 @@ class ILibrary(ILibraryView, MutableMapping[str, 'Pattern'], metaclass=ABCMeta):
return new_name return new_name
def __le__(self, other: Mapping[str, 'Pattern']) -> Abstract: def __le__(self, other: Mapping[str, 'Pattern']) -> Abstract:
"""
Perform the same operation as `__lshift__` / `<<`, but return an `Abstract` instead
of just the pattern's name.
Raises:
LibraryError if there is more than one topcell in `other`.
"""
new_name = self << other new_name = self << other
return self.abstract(new_name) return self.abstract(new_name)
@ -865,7 +827,7 @@ class ILibrary(ILibraryView, MutableMapping[str, 'Pattern'], metaclass=ABCMeta):
if name_func is None: if name_func is None:
def name_func(_pat, _shape): def name_func(_pat, _shape):
return self.get_name(SINGLE_USE_PREFIX + 'rep') return self.get_name(SINGLE_USE_PREFIX = 'rep')
for pat in tuple(self.values()): for pat in tuple(self.values()):
for layer in pat.shapes: for layer in pat.shapes: