From 4d5851604996b8f451c7444e365d3b2eedb2051c Mon Sep 17 00:00:00 2001 From: jan Date: Thu, 7 Jul 2022 14:12:46 -0700 Subject: [PATCH] Remove object locking/unlocking. - It was *slow*. Often >50% of runtime for large designs. - It didn't catch all corner cases. True immutability would require language-level support. - (minor) It doesn't play nice with type checking via mypy. --- masque/__init__.py | 3 +- masque/error.py | 7 --- masque/file/dxf.py | 4 +- masque/file/gdsii.py | 4 +- masque/file/oasis.py | 4 +- masque/file/python_gdsii.py | 4 +- masque/label.py | 42 ++++---------- masque/pattern.py | 106 +++++++--------------------------- masque/repetition.py | 90 ++++------------------------- masque/shapes/arc.py | 30 +--------- masque/shapes/circle.py | 16 +---- masque/shapes/ellipse.py | 27 +-------- masque/shapes/path.py | 23 +------- masque/shapes/polygon.py | 27 +-------- masque/shapes/shape.py | 28 ++------- masque/shapes/text.py | 27 +-------- masque/subpattern.py | 98 ++++++------------------------- masque/traits/__init__.py | 1 - masque/traits/annotatable.py | 3 - masque/traits/lockable.py | 103 --------------------------------- masque/traits/positionable.py | 20 ------- 21 files changed, 79 insertions(+), 588 deletions(-) delete mode 100644 masque/traits/lockable.py diff --git a/masque/__init__.py b/masque/__init__.py index 7881bdb..da0ecde 100644 --- a/masque/__init__.py +++ b/masque/__init__.py @@ -24,11 +24,10 @@ metaclass is used to auto-generate slots based on superclass type annotations. - File I/O submodules are imported by `masque.file` to avoid creating hard dependencies on external file-format reader/writers - - Pattern locking/unlocking is quite slow for large hierarchies. """ -from .error import PatternError, PatternLockedError +from .error import PatternError from .shapes import Shape from .label import Label from .subpattern import SubPattern diff --git a/masque/error.py b/masque/error.py index 54290f9..3cbd0f7 100644 --- a/masque/error.py +++ b/masque/error.py @@ -11,13 +11,6 @@ class PatternError(MasqueError): """ pass -class PatternLockedError(PatternError): - """ - Exception raised when trying to modify a locked pattern - """ - def __init__(self): - PatternError.__init__(self, 'Tried to modify a locked Pattern, subpattern, or shape') - class LibraryError(MasqueError): """ diff --git a/masque/file/dxf.py b/masque/file/dxf.py index 4a6b9e3..462538d 100644 --- a/masque/file/dxf.py +++ b/masque/file/dxf.py @@ -63,7 +63,7 @@ def write( patterns: A Pattern or list of patterns to write to the stream. stream: Stream object to write to. modify_original: If `True`, the original pattern is modified as part of the writing - process. Otherwise, a copy is made and `deepunlock()`-ed. + process. Otherwise, a copy is made. Default `False`. disambiguate_func: Function which takes a list of patterns and alters them to make their names valid and unique. Default is `disambiguate_pattern_names`. @@ -75,7 +75,7 @@ def write( assert(disambiguate_func is not None) if not modify_originals: - pattern = pattern.deepcopy().deepunlock() + pattern = pattern.deepcopy() # Get a dict of id(pattern) -> pattern patterns_by_id = pattern.referenced_patterns_by_id() diff --git a/masque/file/gdsii.py b/masque/file/gdsii.py index 6bd4d1a..c72b7e3 100644 --- a/masque/file/gdsii.py +++ b/masque/file/gdsii.py @@ -94,7 +94,7 @@ def write( library_name: Library name written into the GDSII file. Default 'masque-klamath'. modify_originals: If `True`, the original pattern is modified as part of the writing - process. Otherwise, a copy is made and `deepunlock()`-ed. + process. Otherwise, a copy is made. Default `False`. disambiguate_func: Function which takes a list of patterns and alters them to make their names valid and unique. Default is `disambiguate_pattern_names`, which @@ -109,7 +109,7 @@ def write( assert(disambiguate_func is not None) # placate mypy if not modify_originals: - patterns = [p.deepunlock() for p in copy.deepcopy(patterns)] + patterns = copy.deepcopy(patterns) patterns = [p.wrap_repeated_shapes() for p in patterns] diff --git a/masque/file/oasis.py b/masque/file/oasis.py index 27c8b71..f3b0a08 100644 --- a/masque/file/oasis.py +++ b/masque/file/oasis.py @@ -87,7 +87,7 @@ def build( `fatamorgana.records.LayerName` entries. Default is an empty dict (no names provided). modify_originals: If `True`, the original pattern is modified as part of the writing - process. Otherwise, a copy is made and `deepunlock()`-ed. + process. Otherwise, a copy is made. Default `False`. disambiguate_func: Function which takes a list of patterns and alters them to make their names valid and unique. Default is `disambiguate_pattern_names`. @@ -109,7 +109,7 @@ def build( annotations = {} if not modify_originals: - patterns = [p.deepunlock() for p in copy.deepcopy(patterns)] + patterns = copy.deepcopy(patterns) # Create library lib = fatamorgana.OasisLayout(unit=units_per_micron, validation=None) diff --git a/masque/file/python_gdsii.py b/masque/file/python_gdsii.py index 6b89abc..af54277 100644 --- a/masque/file/python_gdsii.py +++ b/masque/file/python_gdsii.py @@ -95,7 +95,7 @@ def build( library_name: Library name written into the GDSII file. Default 'masque-gdsii-write'. modify_originals: If `True`, the original pattern is modified as part of the writing - process. Otherwise, a copy is made and `deepunlock()`-ed. + process. Otherwise, a copy is made. Default `False`. disambiguate_func: Function which takes a list of patterns and alters them to make their names valid and unique. Default is `disambiguate_pattern_names`, which @@ -113,7 +113,7 @@ def build( assert(disambiguate_func is not None) # placate mypy if not modify_originals: - patterns = [p.deepunlock() for p in copy.deepcopy(patterns)] + patterns = copy.deepcopy(patterns) patterns = [p.wrap_repeated_shapes() for p in patterns] diff --git a/masque/label.py b/masque/label.py index fe7d9ab..947adfa 100644 --- a/masque/label.py +++ b/masque/label.py @@ -6,14 +6,13 @@ from numpy.typing import ArrayLike, NDArray from .repetition import Repetition from .utils import rotation_matrix_2d, layer_t, AutoSlots, annotations_t -from .traits import PositionableImpl, LayerableImpl, Copyable, Pivotable, LockableImpl, RepeatableImpl -from .traits import AnnotatableImpl +from .traits import PositionableImpl, LayerableImpl, Copyable, Pivotable, RepeatableImpl, AnnotatableImpl L = TypeVar('L', bound='Label') -class Label(PositionableImpl, LayerableImpl, LockableImpl, RepeatableImpl, AnnotatableImpl, +class Label(PositionableImpl, LayerableImpl, RepeatableImpl, AnnotatableImpl, Pivotable, Copyable, metaclass=AutoSlots): """ A text annotation with a position and layer (but no size; it is not drawn) @@ -49,33 +48,23 @@ class Label(PositionableImpl, LayerableImpl, LockableImpl, RepeatableImpl, Annot layer: layer_t = 0, repetition: Optional[Repetition] = None, annotations: Optional[annotations_t] = None, - locked: bool = False, identifier: Tuple = (), ) -> None: - LockableImpl.unlock(self) self.identifier = identifier self.string = string self.offset = numpy.array(offset, dtype=float, copy=True) self.layer = layer self.repetition = repetition self.annotations = annotations if annotations is not None else {} - self.set_locked(locked) def __copy__(self: L) -> L: - return type(self)(string=self.string, - offset=self.offset.copy(), - layer=self.layer, - repetition=self.repetition, - locked=self.locked, - identifier=self.identifier) - - def __deepcopy__(self: L, memo: Dict = None) -> L: - memo = {} if memo is None else memo - new = copy.copy(self) - LockableImpl.unlock(new) - new._offset = self._offset.copy() - new.set_locked(self.locked) - return new + return type(self)( + string=self.string, + offset=self.offset.copy(), + layer=self.layer, + repetition=self.repetition, + identifier=self.identifier, + ) def rotate_around(self: L, pivot: ArrayLike, rotation: float) -> L: """ @@ -107,16 +96,5 @@ class Label(PositionableImpl, LayerableImpl, LockableImpl, RepeatableImpl, Annot """ return numpy.array([self.offset, self.offset]) - def lock(self: L) -> L: - PositionableImpl._lock(self) - LockableImpl.lock(self) - return self - - def unlock(self: L) -> L: - LockableImpl.unlock(self) - PositionableImpl._unlock(self) - return self - def __repr__(self) -> str: - locked = ' L' if self.locked else '' - return f'