more fixes and improvements

This commit is contained in:
Jan Petykiewicz 2023-01-25 23:19:25 -08:00 committed by jan
parent d9fe295f4f
commit 5452bc5608
11 changed files with 78 additions and 31 deletions

View File

@ -3,11 +3,13 @@ from typing import Dict, TypeVar
import copy import copy
import logging import logging
import numpy
from numpy.typing import ArrayLike from numpy.typing import ArrayLike
#from .pattern import Pattern #from .pattern import Pattern
from .ref import Ref from .ref import Ref
from .ports import PortList, Port from .ports import PortList, Port
from .utils import rotation_matrix_2d, normalize_mirror
#if TYPE_CHECKING: #if TYPE_CHECKING:
# from .builder import Builder, Tool # from .builder import Builder, Tool
@ -203,7 +205,7 @@ class Abstract(PortList):
Returns: Returns:
self self
""" """
mirror_across_x, angle = normalize_mirror(ref.mirrored) mirrored_across_x, angle = normalize_mirror(ref.mirrored)
if mirrored_across_x: if mirrored_across_x:
self.mirror(across_axis=0) self.mirror(across_axis=0)
self.rotate_ports(angle + ref.rotation) self.rotate_ports(angle + ref.rotation)
@ -224,7 +226,7 @@ class Abstract(PortList):
# TODO test undo_ref_transform # TODO test undo_ref_transform
""" """
mirror_across_x, angle = normalize_mirror(ref.mirrored) mirrored_across_x, angle = normalize_mirror(ref.mirrored)
self.translate_ports(-ref.offset) self.translate_ports(-ref.offset)
self.rotate_port_offsets(-angle - ref.rotation) self.rotate_port_offsets(-angle - ref.rotation)
self.rotate_ports(-angle - ref.rotation) self.rotate_ports(-angle - ref.rotation)

View File

@ -13,6 +13,7 @@ from ..library import MutableLibrary
from ..error import PortError, BuildError from ..error import PortError, BuildError
from ..ports import PortList, Port from ..ports import PortList, Port
from ..abstract import Abstract from ..abstract import Abstract
from ..utils import SupportsBool
from .tools import Tool from .tools import Tool
from .utils import ell from .utils import ell
@ -148,7 +149,7 @@ class Builder(PortList):
@classmethod @classmethod
def interface( def interface(
cls, cls,
source: Union[PortList, Mapping[str, Port]], source: Union[PortList, Mapping[str, Port], str],
*, *,
library: Optional[MutableLibrary] = None, library: Optional[MutableLibrary] = None,
tools: Union[None, Tool, MutableMapping[Optional[str], Tool]] = None, tools: Union[None, Tool, MutableMapping[Optional[str], Tool]] = None,
@ -215,7 +216,9 @@ class Builder(PortList):
if tools is None and hasattr(source, 'tools') and isinstance(source.tools, dict): if tools is None and hasattr(source, 'tools') and isinstance(source.tools, dict):
tools = source.tools tools = source.tools
if isinstance(source, PortList): if isinstance(source, str):
orig_ports = library.abstract(source).ports
elif isinstance(source, PortList):
orig_ports = source.ports orig_ports = source.ports
elif isinstance(source, dict): elif isinstance(source, dict):
orig_ports = source orig_ports = source
@ -250,7 +253,7 @@ class Builder(PortList):
def plug( def plug(
self: BB, self: BB,
other: Abstract, other: Union[Abstract, str],
map_in: Dict[str, str], map_in: Dict[str, str],
map_out: Optional[Dict[str, Optional[str]]] = None, map_out: Optional[Dict[str, Optional[str]]] = None,
*, *,
@ -280,7 +283,7 @@ class Builder(PortList):
having to provide `map_out` each time `plug` is called. having to provide `map_out` each time `plug` is called.
Args: Args:
other: A `DeviceRef` describing the device to be instatiated. other: An `Abstract` describing the device to be instatiated.
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
@ -315,6 +318,9 @@ class Builder(PortList):
logger.error('Skipping plug() since device is dead') logger.error('Skipping plug() since device is dead')
return self return self
if isinstance(other, str):
other = self.library.abstract(other)
if (inherit_name if (inherit_name
and not map_out and not map_out
and len(map_in) == 1 and len(map_in) == 1
@ -345,7 +351,7 @@ class Builder(PortList):
def place( def place(
self: BB, self: BB,
other: Abstract, other: Union[Abstract, str],
*, *,
offset: ArrayLike = (0, 0), offset: ArrayLike = (0, 0),
rotation: float = 0, rotation: float = 0,
@ -369,7 +375,7 @@ class Builder(PortList):
rather than the port name on the original `pad` device. rather than the port name on the original `pad` device.
Args: Args:
other: A `DeviceRef` describing the device to be instatiated. other: An `Abstract` describing the device to be instatiated.
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)).
@ -395,6 +401,9 @@ class Builder(PortList):
logger.error('Skipping place() since device is dead') logger.error('Skipping place() since device is dead')
return self return self
if isinstance(other, str):
other = self.library.abstract(other)
if port_map is None: if port_map is None:
port_map = {} port_map = {}
@ -508,7 +517,7 @@ class Builder(PortList):
def path( def path(
self: BB, self: BB,
portspec: str, portspec: str,
ccw: Optional[bool], ccw: Optional[SupportsBool],
length: float, length: float,
*, *,
tool_port_names: Sequence[str] = ('A', 'B'), tool_port_names: Sequence[str] = ('A', 'B'),
@ -529,7 +538,7 @@ class Builder(PortList):
def path_to( def path_to(
self: BB, self: BB,
portspec: str, portspec: str,
ccw: Optional[bool], ccw: Optional[SupportsBool],
position: float, position: float,
*, *,
tool_port_names: Sequence[str] = ('A', 'B'), tool_port_names: Sequence[str] = ('A', 'B'),
@ -563,7 +572,7 @@ class Builder(PortList):
def mpath( def mpath(
self: BB, self: BB,
portspec: Union[str, Sequence[str]], portspec: Union[str, Sequence[str]],
ccw: Optional[bool], ccw: Optional[SupportsBool],
*, *,
spacing: Optional[Union[float, ArrayLike]] = None, spacing: Optional[Union[float, ArrayLike]] = None,
set_rotation: Optional[float] = None, set_rotation: Optional[float] = None,
@ -611,4 +620,13 @@ class Builder(PortList):
# TODO def path_join() and def bus_join()? # TODO def path_join() and def bus_join()?
def flatten(self: BB) -> BB:
"""
Flatten the contained pattern, using the contained library to resolve references.
Returns:
self
"""
self.pattern.flatten(self.library)
return self

View File

@ -5,15 +5,14 @@ Functions for writing port data into a Pattern (`dev2pat`) and retrieving it (`p
the port locations. This particular approach is just a sensible default; feel free to the port locations. This particular approach is just a sensible default; feel free to
to write equivalent functions for your own format or alternate storage methods. to write equivalent functions for your own format or alternate storage methods.
""" """
from typing import Sequence, Optional, Mapping, Tuple, Dict from typing import Sequence, Optional, Mapping
import logging import logging
import numpy import numpy
from numpy.typing import NDArray
from ..pattern import Pattern from ..pattern import Pattern
from ..label import Label from ..label import Label
from ..utils import rotation_matrix_2d, layer_t from ..utils import layer_t
from ..ports import Port from ..ports import Port
from ..error import PatternError from ..error import PatternError
from ..library import Library, WrapROLibrary from ..library import Library, WrapROLibrary
@ -79,7 +78,6 @@ def pat2dev(
to contain their own port info without interfering with supercells' to contain their own port info without interfering with supercells'
port data. port data.
Default True. Default True.
blacklist: If a cell name appears in the blacklist, do not ea
Returns: Returns:
The updated `pattern`. Port labels are not removed. The updated `pattern`. Port labels are not removed.
@ -99,6 +97,8 @@ def pat2dev(
# Load ports for all subpatterns, and use any we find # Load ports for all subpatterns, and use any we find
found_ports = False found_ports = False
for target in set(rr.target for rr in pattern.refs): for target in set(rr.target for rr in pattern.refs):
if target is None:
continue
pp = pat2dev( pp = pat2dev(
layers=layers, layers=layers,
library=library, library=library,
@ -106,7 +106,6 @@ def pat2dev(
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},
) )
found_ports |= bool(pp.ports) found_ports |= bool(pp.ports)
@ -114,6 +113,8 @@ def pat2dev(
return pattern return pattern
for ref in pattern.refs: for ref in pattern.refs:
if ref.target is None:
continue
aa = library.abstract(ref.target) aa = library.abstract(ref.target)
if not aa.ports: if not aa.ports:
continue continue

View File

@ -3,6 +3,8 @@ Tools are objects which dynamically generate simple single-use devices (e.g. wir
""" """
from typing import TYPE_CHECKING, Optional, Sequence from typing import TYPE_CHECKING, Optional, Sequence
from ..utils import SupportsBool
if TYPE_CHECKING: if TYPE_CHECKING:
from ..pattern import Pattern from ..pattern import Pattern
@ -10,7 +12,7 @@ if TYPE_CHECKING:
class Tool: class Tool:
def path( def path(
self, self,
ccw: Optional[bool], ccw: Optional[SupportsBool],
length: float, length: float,
*, *,
in_ptype: Optional[str] = None, in_ptype: Optional[str] = None,

View File

@ -6,7 +6,7 @@ import numpy
from numpy import pi from numpy import pi
from numpy.typing import ArrayLike, NDArray from numpy.typing import ArrayLike, NDArray
from ..utils import rotation_matrix_2d from ..utils import rotation_matrix_2d, SupportsBool
from ..error import BuildError from ..error import BuildError
if TYPE_CHECKING: if TYPE_CHECKING:
@ -15,7 +15,7 @@ if TYPE_CHECKING:
def ell( def ell(
ports: Mapping[str, 'Port'], ports: Mapping[str, 'Port'],
ccw: Optional[bool], ccw: Optional[SupportsBool],
bound_type: str, bound_type: str,
bound: Union[float, ArrayLike], bound: Union[float, ArrayLike],
*, *,

View File

@ -571,7 +571,7 @@ def load_libraryfile(
*, *,
use_mmap: bool = True, use_mmap: bool = True,
full_load: bool = False, full_load: bool = False,
postprocess: Optional[Callable[[Library, str], Pattern]] = None postprocess: Optional[Callable[[Library, str, Pattern], Pattern]] = None
) -> Tuple[LazyLibrary, Dict[str, Any]]: ) -> Tuple[LazyLibrary, Dict[str, Any]]:
""" """
Wrapper for `load_library()` that takes a filename or path instead of a stream. Wrapper for `load_library()` that takes a filename or path instead of a stream.

View File

@ -5,7 +5,7 @@ Library classes for managing unique name->pattern mappings and
# TODO documentn all library classes # TODO documentn all library classes
# TODO toplevel documentation of library, classes, and abstracts # TODO toplevel documentation of library, classes, and abstracts
""" """
from typing import List, Dict, Callable, TypeVar, Generic, Type, TYPE_CHECKING from typing import List, Dict, Callable, TypeVar, Generic, Type, TYPE_CHECKING, cast
from typing import Tuple, Union, Iterator, Mapping, MutableMapping, Set, Optional, Sequence from typing import Tuple, Union, Iterator, Mapping, MutableMapping, Set, Optional, Sequence
import logging import logging
import base64 import base64
@ -396,14 +396,14 @@ class Library(Mapping[str, 'Pattern'], metaclass=ABCMeta):
if pattern is not original_pattern: if pattern is not original_pattern:
name = hierarchy[-1] name = hierarchy[-1]
if not isintance(self, MutableLibrary): if not isinstance(self, MutableLibrary):
raise LibraryError('visit_* functions returned a new `Pattern` object' raise LibraryError('visit_* functions returned a new `Pattern` object'
' but the library is immutable') ' but the library is immutable')
if name is None: if name is None:
raise LibraryError('visit_* functions returned a new `Pattern` object' raise LibraryError('visit_* functions returned a new `Pattern` object'
' but no top-level name was provided in `hierarchy`') ' but no top-level name was provided in `hierarchy`')
self.set_const(name, pattern) cast(MutableLibrary, self).set_const(name, pattern)
return self return self

View File

@ -13,9 +13,9 @@ from numpy.typing import NDArray, ArrayLike
# .visualize imports matplotlib and matplotlib.collections # .visualize imports matplotlib and matplotlib.collections
from .ref import Ref from .ref import Ref
from .shapes import Shape from .shapes import Shape, Polygon
from .label import Label from .label import Label
from .utils import rotation_matrix_2d, AutoSlots, annotations_t from .utils import rotation_matrix_2d, annotations_t
from .error import PatternError from .error import PatternError
from .traits import AnnotatableImpl, Scalable, Mirrorable, Rotatable, Positionable, Repeatable from .traits import AnnotatableImpl, Scalable, Mirrorable, Rotatable, Positionable, Repeatable
from .ports import Port, PortList from .ports import Port, PortList
@ -24,7 +24,7 @@ from .ports import Port, PortList
P = TypeVar('P', bound='Pattern') P = TypeVar('P', bound='Pattern')
class Pattern(PortList, AnnotatableImpl, Mirrorable, metaclass=AutoSlots): class Pattern(PortList, AnnotatableImpl, Mirrorable):
""" """
2D layout consisting of some set of shapes, labels, and references to other Pattern objects 2D layout consisting of some set of shapes, labels, and references to other Pattern objects
(via Ref). Shapes are assumed to inherit from masque.shapes.Shape or provide equivalent functions. (via Ref). Shapes are assumed to inherit from masque.shapes.Shape or provide equivalent functions.
@ -534,6 +534,21 @@ class Pattern(PortList, AnnotatableImpl, Mirrorable, metaclass=AutoSlots):
self.refs.append(Ref(*args, **kwargs)) self.refs.append(Ref(*args, **kwargs))
return self return self
def rect(self: P, *args: Any, **kwargs: Any) -> P:
"""
Convenience function which calls `Polygon.rect` to construct a
rectangle and adds it to this pattern.
Args:
*args: Passed to `Polygon.rect()`
**kwargs: Passed to `Polygon.rect()`
Returns:
self
"""
self.shapes.append(Polygon.rect(*args, **kwargs))
return self
def flatten( def flatten(
self: P, self: P,
library: Mapping[str, P], library: Mapping[str, P],

View File

@ -11,7 +11,7 @@ from numpy import pi
from numpy.typing import ArrayLike, NDArray from numpy.typing import ArrayLike, NDArray
from .traits import PositionableImpl, Rotatable, PivotableImpl, Copyable, Mirrorable from .traits import PositionableImpl, Rotatable, PivotableImpl, Copyable, Mirrorable
from .utils import AutoSlots, rotate_offsets_around from .utils import rotate_offsets_around
from .error import PortError from .error import PortError
@ -23,7 +23,7 @@ PL = TypeVar('PL', bound='PortList')
PL2 = TypeVar('PL2', bound='PortList') PL2 = TypeVar('PL2', bound='PortList')
class Port(PositionableImpl, Rotatable, PivotableImpl, Copyable, Mirrorable, metaclass=AutoSlots): class Port(PositionableImpl, Rotatable, PivotableImpl, Copyable, Mirrorable):
""" """
A point at which a `Device` can be snapped to another `Device`. A point at which a `Device` can be snapped to another `Device`.
@ -37,7 +37,11 @@ class Port(PositionableImpl, Rotatable, PivotableImpl, Copyable, Mirrorable, met
The `ptype` is an arbitrary string, default of `unk` (unknown). The `ptype` is an arbitrary string, default of `unk` (unknown).
""" """
__slots__ = ('ptype', '_rotation') __slots__ = (
'ptype', '_rotation',
# inherited:
'_offset',
)
_rotation: Optional[float] _rotation: Optional[float]
""" radians counterclockwise from +x, pointing into device body. """ radians counterclockwise from +x, pointing into device body.

View File

@ -1,7 +1,7 @@
""" """
Various helper functions, type definitions, etc. Various helper functions, type definitions, etc.
""" """
from .types import layer_t, annotations_t from .types import layer_t, annotations_t, SupportsBool
from .array import is_scalar from .array import is_scalar
from .autoslots import AutoSlots from .autoslots import AutoSlots

View File

@ -1,8 +1,13 @@
""" """
Type definitions Type definitions
""" """
from typing import Union, Tuple, Dict, List from typing import Union, Tuple, Dict, List, Protocol
layer_t = Union[int, Tuple[int, int], str] layer_t = Union[int, Tuple[int, int], str]
annotations_t = Dict[str, List[Union[int, float, str]]] annotations_t = Dict[str, List[Union[int, float, str]]]
class SupportsBool(Protocol):
def __bool__(self) -> bool:
...