Update comments
This commit is contained in:
parent
ed10f57a31
commit
94300d926a
@ -1,5 +1,7 @@
|
|||||||
"""
|
"""
|
||||||
Tools are objects which dynamically generate simple single-use devices (e.g. wires or waveguides)
|
Tools are objects which dynamically generate simple single-use devices (e.g. wires or waveguides)
|
||||||
|
|
||||||
|
# TODO document all tools
|
||||||
"""
|
"""
|
||||||
from typing import Sequence, Literal, Callable, Any
|
from typing import Sequence, Literal, Callable, Any
|
||||||
from abc import ABCMeta, abstractmethod # TODO any way to make Tool ok with implementing only one method?
|
from abc import ABCMeta, abstractmethod # TODO any way to make Tool ok with implementing only one method?
|
||||||
|
@ -36,7 +36,21 @@ visitor_function_t = Callable[..., 'Pattern']
|
|||||||
|
|
||||||
|
|
||||||
def _rename_patterns(lib: 'ILibraryView', name: str) -> str:
|
def _rename_patterns(lib: 'ILibraryView', name: str) -> str:
|
||||||
# TODO document rename function
|
"""
|
||||||
|
The default `rename_theirs` function for `ILibrary.add`.
|
||||||
|
|
||||||
|
Treats names starting with an underscore as "one-offs" for which name conflicts
|
||||||
|
should be automatically resolved. Conflicts are resolved by calling
|
||||||
|
`lib.get_name(name.split('$')[0])`.
|
||||||
|
Names without a leading underscore are directly returned.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
lib: The library into which `name` is to be added (but is presumed to conflict)
|
||||||
|
name: The original name, to be modified
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
The new name, not guaranteed to be conflict-free!
|
||||||
|
"""
|
||||||
if not name.startswith('_'): # TODO what are the consequences of making '_' special? maybe we can make this decision everywhere?
|
if not name.startswith('_'): # TODO what are the consequences of making '_' special? maybe we can make this decision everywhere?
|
||||||
return name
|
return name
|
||||||
|
|
||||||
@ -45,6 +59,11 @@ def _rename_patterns(lib: 'ILibraryView', name: str) -> str:
|
|||||||
|
|
||||||
|
|
||||||
class ILibraryView(Mapping[str, 'Pattern'], metaclass=ABCMeta):
|
class ILibraryView(Mapping[str, 'Pattern'], metaclass=ABCMeta):
|
||||||
|
"""
|
||||||
|
Interface for a read-only library.
|
||||||
|
|
||||||
|
A library is a mapping from unique names (str) to collections of geometry (`Pattern`).
|
||||||
|
"""
|
||||||
# inherited abstract functions
|
# inherited abstract functions
|
||||||
#def __getitem__(self, key: str) -> 'Pattern':
|
#def __getitem__(self, key: str) -> 'Pattern':
|
||||||
#def __iter__(self) -> Iterator[str]:
|
#def __iter__(self) -> Iterator[str]:
|
||||||
@ -53,6 +72,10 @@ class ILibraryView(Mapping[str, 'Pattern'], metaclass=ABCMeta):
|
|||||||
#__contains__, keys, items, values, get, __eq__, __ne__ supplied by Mapping
|
#__contains__, keys, items, values, get, __eq__, __ne__ supplied by Mapping
|
||||||
|
|
||||||
def abstract_view(self) -> 'AbstractView':
|
def abstract_view(self) -> 'AbstractView':
|
||||||
|
"""
|
||||||
|
Returns:
|
||||||
|
An AbstractView into this library
|
||||||
|
"""
|
||||||
return AbstractView(self)
|
return AbstractView(self)
|
||||||
|
|
||||||
def abstract(self, name: str) -> Abstract:
|
def abstract(self, name: str) -> Abstract:
|
||||||
@ -205,14 +228,19 @@ class ILibraryView(Mapping[str, 'Pattern'], metaclass=ABCMeta):
|
|||||||
def flatten(
|
def flatten(
|
||||||
self,
|
self,
|
||||||
tops: str | Sequence[str],
|
tops: str | Sequence[str],
|
||||||
flatten_ports: bool = False, # TODO document
|
flatten_ports: bool = False,
|
||||||
) -> dict[str, 'Pattern']:
|
) -> dict[str, 'Pattern']:
|
||||||
"""
|
"""
|
||||||
Removes all refs and adds equivalent shapes.
|
Returns copies of all `tops` patterns with all refs
|
||||||
Also flattens all referenced patterns.
|
removed and replaced with equivalent shapes.
|
||||||
|
Also returns flattened copies of all referenced patterns.
|
||||||
|
The originals in the calling `Library` are not modified.
|
||||||
|
For an in-place variant, see `Pattern.flatten`.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
tops: The pattern(s) to flattern.
|
tops: The pattern(s) to flattern.
|
||||||
|
flatten_ports: If `True`, keep ports from any referenced
|
||||||
|
patterns; otherwise discard them.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
{name: flat_pattern} mapping for all flattened patterns.
|
{name: flat_pattern} mapping for all flattened patterns.
|
||||||
@ -222,7 +250,7 @@ class ILibraryView(Mapping[str, 'Pattern'], metaclass=ABCMeta):
|
|||||||
|
|
||||||
flattened: dict[str, 'Pattern | None'] = {}
|
flattened: dict[str, 'Pattern | None'] = {}
|
||||||
|
|
||||||
def flatten_single(name) -> None:
|
def flatten_single(name: str) -> None:
|
||||||
flattened[name] = None
|
flattened[name] = None
|
||||||
pat = self[name].deepcopy()
|
pat = self[name].deepcopy()
|
||||||
|
|
||||||
@ -428,11 +456,12 @@ class ILibraryView(Mapping[str, 'Pattern'], metaclass=ABCMeta):
|
|||||||
pattern = visit_after(pattern, hierarchy=hierarchy, memo=memo, transform=transform)
|
pattern = visit_after(pattern, hierarchy=hierarchy, memo=memo, transform=transform)
|
||||||
|
|
||||||
if pattern is not original_pattern:
|
if pattern is not original_pattern:
|
||||||
name = hierarchy[-1] # TODO what is name=None?
|
name = hierarchy[-1]
|
||||||
if not isinstance(self, ILibrary):
|
if not isinstance(self, ILibrary):
|
||||||
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:
|
||||||
|
# The top pattern is not the original pattern, but we don't know what to call it!
|
||||||
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`')
|
||||||
|
|
||||||
@ -442,6 +471,11 @@ class ILibraryView(Mapping[str, 'Pattern'], metaclass=ABCMeta):
|
|||||||
|
|
||||||
|
|
||||||
class ILibrary(ILibraryView, MutableMapping[str, 'Pattern'], metaclass=ABCMeta):
|
class ILibrary(ILibraryView, MutableMapping[str, 'Pattern'], metaclass=ABCMeta):
|
||||||
|
"""
|
||||||
|
Interface for a writeable library.
|
||||||
|
|
||||||
|
A library is a mapping from unique names (str) to collections of geometry (`Pattern`).
|
||||||
|
"""
|
||||||
# inherited abstract functions
|
# inherited abstract functions
|
||||||
#def __getitem__(self, key: str) -> 'Pattern':
|
#def __getitem__(self, key: str) -> 'Pattern':
|
||||||
#def __iter__(self) -> Iterator[str]:
|
#def __iter__(self) -> Iterator[str]:
|
||||||
@ -477,7 +511,8 @@ class ILibrary(ILibraryView, MutableMapping[str, 'Pattern'], metaclass=ABCMeta):
|
|||||||
Args:
|
Args:
|
||||||
old_name: Current name for the pattern
|
old_name: Current name for the pattern
|
||||||
new_name: New name for the pattern
|
new_name: New name for the pattern
|
||||||
#TODO move_Reference
|
move_references: If `True`, any refs in this library pointing to `old_name`
|
||||||
|
will be updated to point to `new_name`.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
self
|
self
|
||||||
@ -534,19 +569,31 @@ class ILibrary(ILibraryView, MutableMapping[str, 'Pattern'], metaclass=ABCMeta):
|
|||||||
rename_theirs: Callable[['ILibraryView', str], str] = _rename_patterns,
|
rename_theirs: Callable[['ILibraryView', str], str] = _rename_patterns,
|
||||||
) -> dict[str, str]:
|
) -> dict[str, str]:
|
||||||
"""
|
"""
|
||||||
Add keys from another library into this one.
|
Add items from another library into this one.
|
||||||
|
|
||||||
# TODO explain reference renaming and return
|
If any name in `other` is already present in `self`, `rename_theirs(self, name)` is called
|
||||||
|
to pick a new name for the newly-added pattern. If the new name still conflicts with a name
|
||||||
|
in `self` a `LibraryError` is raised. All references to the original name (within `other)`
|
||||||
|
are updated to the new name.
|
||||||
|
|
||||||
|
By default, `rename_theirs` makes no changes to the name (causing a `LibraryError`) unless the
|
||||||
|
name starts with an underscore. Underscored names are truncated to before their first '$'
|
||||||
|
and then passed to `self.get_name()` to create a new unique name.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
other: The library to insert keys from
|
other: The library to insert keys from.
|
||||||
rename_theirs: Called as rename_theirs(self, name) for each duplicate name
|
rename_theirs: Called as rename_theirs(self, name) for each duplicate name
|
||||||
encountered in `other`. Should return the new name for the pattern in
|
encountered in `other`. Should return the new name for the pattern in
|
||||||
`other`.
|
`other`.
|
||||||
Default is effectively
|
Default is effectively
|
||||||
`name.split('$')[0] if name.startswith('_') else name`
|
`self.get_name(name.split('$')[0]) if name.startswith('_') else name`
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
self
|
A mapping of `{old_name: new_name}` for all `old_name`s in `other`. Unchanged
|
||||||
|
names map to themselves.
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
`LibraryError` if a duplicate name is encountered even after applying `rename_theirs()`.
|
||||||
"""
|
"""
|
||||||
from .pattern import map_targets
|
from .pattern import map_targets
|
||||||
duplicates = set(self.keys()) & set(other.keys())
|
duplicates = set(self.keys()) & set(other.keys())
|
||||||
@ -785,7 +832,15 @@ class ILibrary(ILibraryView, MutableMapping[str, 'Pattern'], metaclass=ABCMeta):
|
|||||||
self,
|
self,
|
||||||
repeat: bool = True,
|
repeat: bool = True,
|
||||||
) -> set[str]:
|
) -> set[str]:
|
||||||
# TODO doc prune_empty
|
"""
|
||||||
|
Delete any empty patterns (i.e. where `Pattern.is_empty` returns `True`).
|
||||||
|
|
||||||
|
Args:
|
||||||
|
repeat: Also recursively delete any patterns which only contain(ed) empty patterns.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
A set containing the names of all deleted patterns
|
||||||
|
"""
|
||||||
trimmed = set()
|
trimmed = set()
|
||||||
while empty := set(name for name, pat in self.items() if pat.is_empty()):
|
while empty := set(name for name, pat in self.items() if pat.is_empty()):
|
||||||
for name in empty:
|
for name in empty:
|
||||||
@ -807,7 +862,13 @@ class ILibrary(ILibraryView, MutableMapping[str, 'Pattern'], metaclass=ABCMeta):
|
|||||||
key: str,
|
key: str,
|
||||||
delete_refs: bool = True,
|
delete_refs: bool = True,
|
||||||
) -> Self:
|
) -> Self:
|
||||||
# TODO doc delete()
|
"""
|
||||||
|
Delete a pattern and (optionally) all refs pointing to that pattern.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
key: Name of the pattern to be deleted.
|
||||||
|
delete_refs: If `True` (default), also delete all refs pointing to the pattern.
|
||||||
|
"""
|
||||||
del self[key]
|
del self[key]
|
||||||
if delete_refs:
|
if delete_refs:
|
||||||
for pat in self.values():
|
for pat in self.values():
|
||||||
@ -817,6 +878,12 @@ class ILibrary(ILibraryView, MutableMapping[str, 'Pattern'], metaclass=ABCMeta):
|
|||||||
|
|
||||||
|
|
||||||
class LibraryView(ILibraryView):
|
class LibraryView(ILibraryView):
|
||||||
|
"""
|
||||||
|
Default implementation for a read-only library.
|
||||||
|
|
||||||
|
A library is a mapping from unique names (str) to collections of geometry (`Pattern`).
|
||||||
|
This library is backed by an arbitrary python object which implements the `Mapping` interface.
|
||||||
|
"""
|
||||||
mapping: Mapping[str, 'Pattern']
|
mapping: Mapping[str, 'Pattern']
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
@ -842,6 +909,12 @@ class LibraryView(ILibraryView):
|
|||||||
|
|
||||||
|
|
||||||
class Library(ILibrary):
|
class Library(ILibrary):
|
||||||
|
"""
|
||||||
|
Default implementation for a writeable library.
|
||||||
|
|
||||||
|
A library is a mapping from unique names (str) to collections of geometry (`Pattern`).
|
||||||
|
This library is backed by an arbitrary python object which implements the `MutableMapping` interface.
|
||||||
|
"""
|
||||||
mapping: MutableMapping[str, 'Pattern']
|
mapping: MutableMapping[str, 'Pattern']
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
@ -892,6 +965,12 @@ class Library(ILibrary):
|
|||||||
def mktree(cls, name: str) -> tuple[Self, 'Pattern']:
|
def mktree(cls, name: str) -> tuple[Self, 'Pattern']:
|
||||||
"""
|
"""
|
||||||
Create a new Library and immediately add a pattern
|
Create a new Library and immediately add a pattern
|
||||||
|
|
||||||
|
Args:
|
||||||
|
name: The name for the new pattern (usually the name of the topcell).
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
The newly created `Library` and the newly created `Pattern`
|
||||||
"""
|
"""
|
||||||
from .pattern import Pattern
|
from .pattern import Pattern
|
||||||
tree = cls()
|
tree = cls()
|
||||||
@ -1041,6 +1120,11 @@ class LazyLibrary(ILibrary):
|
|||||||
|
|
||||||
|
|
||||||
class AbstractView(Mapping[str, Abstract]):
|
class AbstractView(Mapping[str, Abstract]):
|
||||||
|
"""
|
||||||
|
A read-only mapping from names to `Abstract` objects.
|
||||||
|
|
||||||
|
This is usually just used as a shorthand for repeated calls to `library.abstract()`.
|
||||||
|
"""
|
||||||
library: ILibraryView
|
library: ILibraryView
|
||||||
|
|
||||||
def __init__(self, library: ILibraryView) -> None:
|
def __init__(self, library: ILibraryView) -> None:
|
||||||
|
@ -318,7 +318,12 @@ class Pattern(PortList, AnnotatableImpl, Mirrorable):
|
|||||||
Returns `None` if the Pattern is empty.
|
Returns `None` if the Pattern is empty.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
TODO docs for get_bounds
|
library: If `recurse=True`, any referenced patterns are loaded from this library.
|
||||||
|
recurse: If `False`, do not evaluate the bounds of any refs (i.e. assume they are empty).
|
||||||
|
If `True`, evaluate the bounds of all refs and their conained geometry recursively.
|
||||||
|
Default `True`.
|
||||||
|
cache: Mapping of `{name: bounds}` for patterns for which the bounds have already been calculated.
|
||||||
|
Modified during the run (any referenced pattern's bounds are added).
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
`[[x_min, y_min], [x_max, y_max]]` or `None`
|
`[[x_min, y_min], [x_max, y_max]]` or `None`
|
||||||
@ -401,7 +406,12 @@ class Pattern(PortList, AnnotatableImpl, Mirrorable):
|
|||||||
Convenience wrapper for `get_bounds()` which asserts that the Pattern as non-None bounds.
|
Convenience wrapper for `get_bounds()` which asserts that the Pattern as non-None bounds.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
TODO docs for get_bounds
|
library: If `recurse=True`, any referenced patterns are loaded from this library.
|
||||||
|
recurse: If `False`, do not evaluate the bounds of any refs (i.e. assume they are empty).
|
||||||
|
If `True`, evaluate the bounds of all refs and their conained geometry recursively.
|
||||||
|
Default `True`.
|
||||||
|
cache: Mapping of `{name: bounds}` for patterns for which the bounds have already been calculated.
|
||||||
|
Modified during the run (any referenced pattern's bounds are added).
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
`[[x_min, y_min], [x_max, y_max]]`
|
`[[x_min, y_min], [x_max, y_max]]`
|
||||||
@ -590,12 +600,24 @@ class Pattern(PortList, AnnotatableImpl, Mirrorable):
|
|||||||
return not (self.has_refs() or self.has_shapes() or self.has_labels())
|
return not (self.has_refs() or self.has_shapes() or self.has_labels())
|
||||||
|
|
||||||
def has_refs(self) -> bool:
|
def has_refs(self) -> bool:
|
||||||
|
"""
|
||||||
|
Returns:
|
||||||
|
True if the pattern contains any refs.
|
||||||
|
"""
|
||||||
return any(True for _ in chain.from_iterable(self.refs.values()))
|
return any(True for _ in chain.from_iterable(self.refs.values()))
|
||||||
|
|
||||||
def has_shapes(self) -> bool:
|
def has_shapes(self) -> bool:
|
||||||
|
"""
|
||||||
|
Returns:
|
||||||
|
True if the pattern contains any shapes.
|
||||||
|
"""
|
||||||
return any(True for _ in chain.from_iterable(self.shapes.values()))
|
return any(True for _ in chain.from_iterable(self.shapes.values()))
|
||||||
|
|
||||||
def has_labels(self) -> bool:
|
def has_labels(self) -> bool:
|
||||||
|
"""
|
||||||
|
Returns:
|
||||||
|
True if the pattern contains any labels.
|
||||||
|
"""
|
||||||
return any(True for _ in chain.from_iterable(self.labels.values()))
|
return any(True for _ in chain.from_iterable(self.labels.values()))
|
||||||
|
|
||||||
def ref(self, target: str | None, *args: Any, **kwargs: Any) -> Self:
|
def ref(self, target: str | None, *args: Any, **kwargs: Any) -> Self:
|
||||||
@ -708,21 +730,23 @@ class Pattern(PortList, AnnotatableImpl, Mirrorable):
|
|||||||
def flatten(
|
def flatten(
|
||||||
self,
|
self,
|
||||||
library: Mapping[str, 'Pattern'],
|
library: Mapping[str, 'Pattern'],
|
||||||
flatten_ports: bool = False, # TODO document
|
flatten_ports: bool = False,
|
||||||
) -> 'Pattern':
|
) -> 'Pattern':
|
||||||
"""
|
"""
|
||||||
Removes all refs (recursively) and adds equivalent shapes.
|
Removes all refs (recursively) and adds equivalent shapes.
|
||||||
Alters the current pattern in-place
|
Alters the current pattern in-place.
|
||||||
|
For a version which creates copies, see `Library.flatten`.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
library: Source for referenced patterns.
|
library: Source for referenced patterns.
|
||||||
|
flatten_ports: If `True`, keep ports from any referenced
|
||||||
|
patterns; otherwise discard them.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
self
|
self
|
||||||
"""
|
"""
|
||||||
flattened: dict[str | None, 'Pattern | None'] = {}
|
flattened: dict[str | None, 'Pattern | None'] = {}
|
||||||
|
|
||||||
# TODO both Library and Pattern have flatten()... pattern is in-place?
|
|
||||||
def flatten_single(name: str | None) -> None:
|
def flatten_single(name: str | None) -> None:
|
||||||
if name is None:
|
if name is None:
|
||||||
pat = self
|
pat = self
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
Ref provides basic support for nesting Pattern objects within each other, by adding
|
Ref provides basic support for nesting Pattern objects within each other, by adding
|
||||||
offset, rotation, scaling, and other such properties to the reference.
|
offset, rotation, scaling, and other such properties to the reference.
|
||||||
"""
|
"""
|
||||||
#TODO more top-level documentation
|
#TODO more top-level documentation for ref
|
||||||
|
|
||||||
from typing import Mapping, TYPE_CHECKING, Self
|
from typing import Mapping, TYPE_CHECKING, Self
|
||||||
import copy
|
import copy
|
||||||
|
@ -17,6 +17,9 @@ class Polygon(Shape):
|
|||||||
A polygon, consisting of a bunch of vertices (Nx2 ndarray) which specify an
|
A polygon, consisting of a bunch of vertices (Nx2 ndarray) which specify an
|
||||||
implicitly-closed boundary, and an offset.
|
implicitly-closed boundary, and an offset.
|
||||||
|
|
||||||
|
Note that the setter for `Polygon.vertices` may (but may not) create a copy of the
|
||||||
|
passed vertex coordinates. See `numpy.array()` for details.
|
||||||
|
|
||||||
A `normalized_form(...)` is available, but can be quite slow with lots of vertices.
|
A `normalized_form(...)` is available, but can be quite slow with lots of vertices.
|
||||||
"""
|
"""
|
||||||
__slots__ = (
|
__slots__ = (
|
||||||
@ -38,7 +41,7 @@ class Polygon(Shape):
|
|||||||
|
|
||||||
@vertices.setter
|
@vertices.setter
|
||||||
def vertices(self, val: ArrayLike) -> None:
|
def vertices(self, val: ArrayLike) -> None:
|
||||||
val = numpy.array(val, dtype=float) # TODO document that these might not be copied
|
val = numpy.array(val, dtype=float) # note that this hopefully won't create a copy
|
||||||
if len(val.shape) < 2 or val.shape[1] != 2:
|
if len(val.shape) < 2 or val.shape[1] != 2:
|
||||||
raise PatternError('Vertices must be an Nx2 array')
|
raise PatternError('Vertices must be an Nx2 array')
|
||||||
if val.shape[0] < 3:
|
if val.shape[0] < 3:
|
||||||
|
@ -60,8 +60,8 @@ def data_to_ports(
|
|||||||
# TODO missing ok?
|
# TODO missing ok?
|
||||||
) -> Pattern:
|
) -> Pattern:
|
||||||
"""
|
"""
|
||||||
# TODO fixup documentation in port_utils
|
# TODO fixup documentation in ports2data
|
||||||
# TODO move port_utils to utils.file?
|
# TODO move to utils.file?
|
||||||
Examine `pattern` for labels specifying port info, and use that info
|
Examine `pattern` for labels specifying port info, and use that info
|
||||||
to fill out its `ports` attribute.
|
to fill out its `ports` attribute.
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user