From 9a7a5583edf29925915ec832a95ca022dfcfc9cc Mon Sep 17 00:00:00 2001 From: jan Date: Fri, 20 Oct 2023 23:14:47 -0700 Subject: [PATCH] Add Tree/TreeView and allow Builder to ingest them --- README.md | 7 ++++++- masque/__init__.py | 2 +- masque/builder/builder.py | 34 ++++++++++++++++++++++++++-------- masque/library.py | 12 ++++++++++-- 4 files changed, 43 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 4565cad..6448b26 100644 --- a/README.md +++ b/README.md @@ -101,7 +101,7 @@ References are accomplished by listing the target's name, not its `Pattern` obje ## Glossary - `Library`: A collection of named cells. OASIS or GDS "library" or file. -- "tree": Any Library which has only one topcell. +- `Tree`: Any `{name: pattern}` mapping which has only one topcell. - `Pattern`: A collection of geometry, text labels, and reference to other patterns. OASIS or GDS "Cell", DXF "Block". - `Ref`: A reference to another pattern. GDS "AREF/SREF", OASIS "Placement". @@ -142,6 +142,11 @@ my_pattern.ref(new_name, ...) # instantiate the cell # In practice, you may do lots of 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: diff --git a/masque/__init__.py b/masque/__init__.py index 3ed2a6f..15210ba 100644 --- a/masque/__init__.py +++ b/masque/__init__.py @@ -38,7 +38,7 @@ from .pattern import Pattern, map_layers, map_targets, chain_elements from .library import ( ILibraryView, ILibrary, LibraryView, Library, LazyLibrary, - AbstractView, + AbstractView, TreeView, Tree, ) from .ports import Port, PortList from .abstract import Abstract diff --git a/masque/builder/builder.py b/masque/builder/builder.py index 68de9e6..a401abc 100644 --- a/masque/builder/builder.py +++ b/masque/builder/builder.py @@ -8,7 +8,7 @@ import logging from numpy.typing import ArrayLike from ..pattern import Pattern -from ..library import ILibrary +from ..library import ILibrary, TreeView from ..error import BuildError from ..ports import PortList, Port from ..abstract import Abstract @@ -190,7 +190,7 @@ class Builder(PortList): def plug( self, - other: Abstract | str | Pattern, + other: Abstract | str | Pattern | TreeView, map_in: dict[str, str], map_out: dict[str, str | None] | None = None, *, @@ -201,11 +201,16 @@ class Builder(PortList): ) -> Self: """ Wrapper around `Pattern.plug` which allows a string for `other`. + The `Builder`'s library is used to dereference the string (or `Abstract`, if - one is passed with `append=True`). + one is passed with `append=True`). If a `TreeView` is passed, it is first + added into `self.library`. Args: - other: An `Abstract`, string, or `Pattern` describing the device to be instatiated. + other: An `Abstract`, string, `Pattern`, or `TreeView` describing the + 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 port connections between the two devices. map_out: dict of `{'old_name': 'new_name'}` mappings, specifying @@ -243,6 +248,10 @@ class Builder(PortList): logger.error('Skipping plug() since device is dead') 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): other = self.library.abstract(other) if append and isinstance(other, Abstract): @@ -261,7 +270,7 @@ class Builder(PortList): def place( self, - other: Abstract | str | Pattern, + other: Abstract | str | Pattern | TreeView, *, offset: ArrayLike = (0, 0), rotation: float = 0, @@ -272,12 +281,17 @@ class Builder(PortList): append: bool = False, ) -> Self: """ - Wrapper around `Pattern.place` which allows a string for `other`. + Wrapper around `Pattern.place` which allows a string or `TreeView` for `other`. + The `Builder`'s library is used to dereference the string (or `Abstract`, if - one is passed with `append=True`). + one is passed with `append=True`). If a `TreeView` is passed, it is first + added into `self.library`. Args: - other: An `Abstract`, string, or `Pattern` describing the device to be instatiated. + other: An `Abstract`, string, `Pattern`, or `TreeView` describing the + 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). rotation: Rotation applied to the instance before placement. Default 0. pivot: Rotation is applied around this pivot point (default (0, 0)). @@ -306,6 +320,10 @@ class Builder(PortList): logger.error('Skipping place() since device is dead') 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): other = self.library.abstract(other) if append and isinstance(other, Abstract): diff --git a/masque/library.py b/masque/library.py index c0d8c93..bee7537 100644 --- a/masque/library.py +++ b/masque/library.py @@ -14,7 +14,7 @@ Classes include: - `AbstractView`: Provides a way to use []-indexing to generate abstracts for patterns in the linked library. Generated with `ILibraryView.abstract_view()`. """ -from typing import Callable, Self, Type, TYPE_CHECKING, cast +from typing import Callable, Self, Type, TYPE_CHECKING, cast, TypeAlias, Protocol, Literal from typing import Iterator, Mapping, MutableMapping, Sequence import logging import base64 @@ -44,6 +44,14 @@ logger = logging.getLogger(__name__) visitor_function_t = Callable[..., '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 = '_' """ Names starting with this prefix are assumed to refer to single-use patterns, @@ -674,7 +682,7 @@ class ILibrary(ILibraryView, MutableMapping[str, 'Pattern'], metaclass=ABCMeta): return rename_map - def __lshift__(self, other: Mapping[str, 'Pattern']) -> str: + def __lshift__(self, other: TreeView) -> str: if len(other) == 1: name = next(iter(other)) else: