wip -- more fixes
This commit is contained in:
parent
9efb6f0eeb
commit
6549faddbb
@ -1,5 +1,5 @@
|
||||
from typing import Dict, Iterable, List, Tuple, Union, TypeVar, Any, Iterator, Optional, Sequence
|
||||
from typing import overload, KeysView, ValuesView
|
||||
from typing import overload, KeysView, ValuesView, MutableMapping
|
||||
import copy
|
||||
import warnings
|
||||
import traceback
|
||||
@ -46,7 +46,11 @@ class PortsRef(PortList):
|
||||
self.name = name
|
||||
self.ports = copy.deepcopy(ports)
|
||||
|
||||
def build(self, library: MutableLibrary) -> 'Builder':
|
||||
def build(
|
||||
self,
|
||||
library: MutableLibrary,
|
||||
tools: Union[None, Tool, MutableMapping[Optional[str], Tool]] = None,
|
||||
) -> 'Builder':
|
||||
"""
|
||||
Begin building a new device around an instance of the current device
|
||||
(rather than modifying the current device).
|
||||
@ -56,7 +60,7 @@ class PortsRef(PortList):
|
||||
"""
|
||||
pat = Pattern(ports=self.ports)
|
||||
pat.ref(self.name)
|
||||
new = Builder(library=library, pattern=pat, tools=self.tools) # TODO should Ref have tools?
|
||||
new = Builder(library=library, pattern=pat, tools=tools) # TODO should Ref have tools?
|
||||
return new
|
||||
|
||||
# TODO do we want to store a Ref instead of just a name? then we can translate/rotate/mirror...
|
||||
@ -148,7 +152,7 @@ class Builder(PortList):
|
||||
library: MutableLibrary,
|
||||
pattern: Optional[Pattern] = None,
|
||||
*,
|
||||
tools: Union[None, Tool, Dict[Optional[str], Tool]] = None,
|
||||
tools: Union[None, Tool, MutableMapping[Optional[str], Tool]] = None,
|
||||
) -> None:
|
||||
"""
|
||||
If `ports` is `None`, two default ports ('A' and 'B') are created.
|
||||
@ -173,7 +177,7 @@ class Builder(PortList):
|
||||
elif isinstance(tools, Tool):
|
||||
self.tools = {None: tools}
|
||||
else:
|
||||
self.tools = tools
|
||||
self.tools = dict(tools)
|
||||
|
||||
self._dead = False
|
||||
|
||||
@ -546,12 +550,12 @@ class Builder(PortList):
|
||||
port_name = tuple(portspec)[0]
|
||||
return self.path(port_name, ccw, extensions[port_name], tool_port_names=tool_port_names)
|
||||
else:
|
||||
bld = ports.as_interface(self.library, tools=self.tools)
|
||||
bld = Pattern(ports=ports).as_interface(self.library, tools=self.tools) # TODO: maybe Builder static as_interface-like should optionally take ports instead? Maybe constructor could do it?
|
||||
for port_name, length in extensions.items():
|
||||
bld.path(port_name, ccw, length, tool_port_names=tool_port_names)
|
||||
name = self.library.get_name(base_name)
|
||||
self.library._set(name, bld.pattern)
|
||||
return self.plug(PortsRef(name, pat.ports), {sp: 'in_' + sp for sp in ports.keys()}) # TODO safe to use 'in_'?
|
||||
return self.plug(PortsRef(name, bld.pattern.ports), {sp: 'in_' + sp for sp in ports.keys()}) # TODO safe to use 'in_'?
|
||||
|
||||
# TODO def path_join() and def bus_join()?
|
||||
|
||||
|
@ -13,13 +13,13 @@ import numpy
|
||||
from ..pattern import Pattern
|
||||
from ..label import Label
|
||||
from ..utils import rotation_matrix_2d, layer_t
|
||||
from .devices import Device, Port
|
||||
from ..ports import Port
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def dev2pat(device: Device, layer: layer_t) -> Pattern:
|
||||
def dev2pat(pattern: Pattern, layer: layer_t) -> Pattern:
|
||||
"""
|
||||
Place a text label at each port location, specifying the port data in the format
|
||||
'name:ptype angle_deg'
|
||||
@ -27,24 +27,24 @@ def dev2pat(device: Device, layer: layer_t) -> Pattern:
|
||||
This can be used to debug port locations or to automatically generate ports
|
||||
when reading in a GDS file.
|
||||
|
||||
NOTE that `device` is modified by this function, and `device.pattern` is returned.
|
||||
NOTE that `pattern` is modified by this function
|
||||
|
||||
Args:
|
||||
device: The device which is to have its ports labeled. MODIFIED in-place.
|
||||
pattern: The pattern which is to have its ports labeled. MODIFIED in-place.
|
||||
layer: The layer on which the labels will be placed.
|
||||
|
||||
Returns:
|
||||
`device.pattern`
|
||||
`pattern`
|
||||
"""
|
||||
for name, port in device.ports.items():
|
||||
for name, port in pattern.ports.items():
|
||||
if port.rotation is None:
|
||||
angle_deg = numpy.inf
|
||||
else:
|
||||
angle_deg = numpy.rad2deg(port.rotation)
|
||||
device.pattern.labels += [
|
||||
pattern.labels += [
|
||||
Label(string=f'{name}:{port.ptype} {angle_deg:g}', layer=layer, offset=port.offset)
|
||||
]
|
||||
return device.pattern
|
||||
return pattern
|
||||
|
||||
|
||||
def pat2dev(
|
||||
@ -53,10 +53,10 @@ def pat2dev(
|
||||
library: Optional[Mapping[str, Pattern]] = None,
|
||||
max_depth: int = 999_999,
|
||||
skip_subcells: bool = True,
|
||||
) -> Device:
|
||||
) -> Pattern:
|
||||
"""
|
||||
Examine `pattern` for labels specifying port info, and use that info
|
||||
to build a `Device` object.
|
||||
to fill out its `ports` attribute.
|
||||
|
||||
Labels are assumed to be placed at the port locations, and have the format
|
||||
'name:ptype angle_deg'
|
||||
@ -68,11 +68,12 @@ def pat2dev(
|
||||
Reduce this to 0 to avoid ever searching subcells.
|
||||
skip_subcells: If port labels are found at a given hierarcy level,
|
||||
do not continue searching at deeper levels. This allows subcells
|
||||
to contain their own port info (and thus become their own Devices).
|
||||
to contain their own port info without interfering with supercells'
|
||||
port data.
|
||||
Default True.
|
||||
|
||||
Returns:
|
||||
The constructed Device object. Port labels are not removed from the pattern.
|
||||
The updated `pattern`. Port labels are not removed.
|
||||
"""
|
||||
ports = {} # Note: could do a list here, if they're not unique
|
||||
annotated_cells = set()
|
||||
@ -109,5 +110,13 @@ def pat2dev(
|
||||
|
||||
return pat
|
||||
|
||||
pattern.dfs(visit_before=find_ports_each, transform=True) #TODO: don't check Library if there are ports in top level
|
||||
return Device(pattern, ports)
|
||||
# TODO TODO TODO
|
||||
if skip_subcells and ports := find_ports_each(pattern, ...):
|
||||
# TODO Could do this with just the `pattern` itself
|
||||
pass
|
||||
else
|
||||
# TODO need `name` and `library` here
|
||||
ports = library.dfs(name, visit_before=find_ports_each, transform=True) #TODO: don't check Library if there are ports in top level
|
||||
pattern.check_ports(other_ports=ports)
|
||||
pattern.ports.update(ports)
|
||||
return pattern
|
||||
|
@ -4,7 +4,7 @@ Tools are objects which dynamically generate simple single-use devices (e.g. wir
|
||||
from typing import TYPE_CHECKING, Optional, Sequence
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from .devices import Device
|
||||
from ..pattern import Pattern
|
||||
|
||||
|
||||
class Tool:
|
||||
@ -17,6 +17,6 @@ class Tool:
|
||||
out_ptype: Optional[str] = None,
|
||||
port_names: Sequence[str] = ('A', 'B'),
|
||||
**kwargs,
|
||||
) -> 'Device':
|
||||
) -> 'Pattern':
|
||||
raise NotImplementedError(f'path() not implemented for {type(self)}')
|
||||
|
||||
|
@ -1,9 +1,10 @@
|
||||
from typing import Dict, Tuple, List, Optional, Union, Any, cast, Sequence, TYPE_CHECKING
|
||||
from typing import Dict, Tuple, List, Mapping, Sequence, SupportsFloat
|
||||
from typing import Optional, Union, Any, cast, TYPE_CHECKING
|
||||
from pprint import pformat
|
||||
|
||||
import numpy
|
||||
from numpy import pi
|
||||
from numpy.typing import ArrayLike
|
||||
from numpy.typing import ArrayLike, NDArray
|
||||
|
||||
from ..utils import rotation_matrix_2d
|
||||
from ..error import BuildError
|
||||
@ -135,6 +136,7 @@ def ell(
|
||||
# D-----------| `d_to_align[3]`
|
||||
#
|
||||
d_to_align = x_start.max() - x_start # distance to travel to align all
|
||||
offsets: NDArray[numpy.float64]
|
||||
if bound_type == 'min_past_furthest':
|
||||
# A------------------V `d_to_exit[0]`
|
||||
# B-----V `d_to_exit[1]`
|
||||
@ -154,6 +156,7 @@ def ell(
|
||||
travel = d_to_align - (ch_offsets.max() - ch_offsets)
|
||||
offsets = travel - travel.min().clip(max=0)
|
||||
|
||||
rot_bound: SupportsFloat
|
||||
if bound_type in ('emin', 'min_extension',
|
||||
'emax', 'max_extension',
|
||||
'min_past_furthest',):
|
||||
|
@ -13,7 +13,7 @@ from numpy import inf
|
||||
from numpy.typing import NDArray, ArrayLike
|
||||
# .visualize imports matplotlib and matplotlib.collections
|
||||
|
||||
from .refs import Ref
|
||||
from .ref import Ref
|
||||
from .shapes import Shape, Polygon
|
||||
from .label import Label
|
||||
from .utils import rotation_matrix_2d, normalize_mirror, AutoSlots, annotations_t
|
||||
@ -56,7 +56,7 @@ class Pattern(PortList, AnnotatableImpl, Mirrorable, metaclass=AutoSlots):
|
||||
labels: Sequence[Label] = (),
|
||||
refs: Sequence[Ref] = (),
|
||||
annotations: Optional[annotations_t] = None,
|
||||
ports: Optional[Mapping[str, Port]] = None
|
||||
ports: Optional[Mapping[str, 'Port']] = None
|
||||
) -> None:
|
||||
"""
|
||||
Basic init; arguments get assigned to member variables.
|
||||
@ -130,8 +130,17 @@ class Pattern(PortList, AnnotatableImpl, Mirrorable, metaclass=AutoSlots):
|
||||
self.refs += other_pattern.refs
|
||||
self.shapes += other_pattern.shapes
|
||||
self.labels += other_pattern.labels
|
||||
self.annotations += other_pattern.annotations
|
||||
self.ports += other_pattern.ports
|
||||
|
||||
annotation_conflicts = set(self.annotations.keys()) & set(other_pattern.annotations.keys())
|
||||
if annotation_conflicts:
|
||||
raise PatternError(f'Annotation keys overlap: {annotation_conflicts}')
|
||||
self.annotations.update(other_pattern.annotations)
|
||||
|
||||
port_conflicts = set(self.ports.keys()) & set(other_pattern.ports.keys())
|
||||
if port_conflicts:
|
||||
raise PatternError(f'Port names overlap: {port_conflicts}')
|
||||
self.ports.update(other_pattern.ports)
|
||||
|
||||
return self
|
||||
|
||||
def subset(
|
||||
@ -139,8 +148,8 @@ class Pattern(PortList, AnnotatableImpl, Mirrorable, metaclass=AutoSlots):
|
||||
shapes: Optional[Callable[[Shape], bool]] = None,
|
||||
labels: Optional[Callable[[Label], bool]] = None,
|
||||
refs: Optional[Callable[[Ref], bool]] = None,
|
||||
annotations: Optional[Callable[[annotation_t], bool]] = None,
|
||||
ports: Optional[Callable[[str], bool]] = None,
|
||||
annotations: Optional[Callable[[str, List[Union[int, float, str]]], bool]] = None,
|
||||
ports: Optional[Callable[[str, Port], bool]] = None,
|
||||
default_keep: bool = False
|
||||
) -> 'Pattern':
|
||||
"""
|
||||
@ -179,12 +188,12 @@ class Pattern(PortList, AnnotatableImpl, Mirrorable, metaclass=AutoSlots):
|
||||
pat.refs = copy.copy(self.refs)
|
||||
|
||||
if annotations is not None:
|
||||
pat.annotations = [s for s in self.annotations if annotations(s)]
|
||||
pat.annotations = {k: v for k, v in self.annotations.items() if annotations(k, v)}
|
||||
elif default_keep:
|
||||
pat.annotations = copy.copy(self.annotations)
|
||||
|
||||
if ports is not None:
|
||||
pat.ports = {k: v for k, v in self.ports.items() if ports(k)}
|
||||
pat.ports = {k: v for k, v in self.ports.items() if ports(k, v)}
|
||||
elif default_keep:
|
||||
pat.ports = copy.copy(self.ports)
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
from typing import Dict, Iterable, List, Tuple, Union, TypeVar, Any, Iterator, Optional, Sequence
|
||||
from typing import overload, KeysView, ValuesView, ItemsView
|
||||
from typing import Dict, Iterable, List, Tuple, Iterator, Optional, Sequence, MutableMapping
|
||||
from typing import overload, KeysView, ValuesView, ItemsView, TYPE_CHECKING, Union, TypeVar, Any
|
||||
import copy
|
||||
import warnings
|
||||
import traceback
|
||||
@ -17,6 +17,9 @@ from .error import DeviceError
|
||||
from .library import MutableLibrary
|
||||
from .builder import Tool
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from .builder import Builder
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@ -117,10 +120,10 @@ class PortList(metaclass=ABCMeta):
|
||||
pass
|
||||
|
||||
@overload
|
||||
def __getitem__(self, key: Union[List[str], Tuple[str, ...], KeysView[str], ValuesView[str]]) -> PortList:
|
||||
def __getitem__(self, key: Union[List[str], Tuple[str, ...], KeysView[str], ValuesView[str]]) -> Dict[str, Port]:
|
||||
pass
|
||||
|
||||
def __getitem__(self, key: Union[str, Iterable[str]]) -> Union[Port, PortList]:
|
||||
def __getitem__(self, key: Union[str, Iterable[str]]) -> Union[Port, Dict[str, Port]]:
|
||||
"""
|
||||
For convenience, ports can be read out using square brackets:
|
||||
- `pattern['A'] == Port((0, 0), 0)`
|
||||
@ -137,7 +140,7 @@ class PortList(metaclass=ABCMeta):
|
||||
return {k: self.ports[k] for k in key}
|
||||
|
||||
# TODO add Mapping stuff to PortsList
|
||||
def keys(self) -> KeysView[Port]:
|
||||
def keys(self) -> KeysView[str]:
|
||||
return self.ports.keys()
|
||||
|
||||
def values(self) -> ValuesView[Port]:
|
||||
@ -250,7 +253,7 @@ class PortList(metaclass=ABCMeta):
|
||||
self,
|
||||
library: MutableLibrary,
|
||||
*,
|
||||
tools: Optional[Dict[str, Tool]] = None,
|
||||
tools: Union[None, Tool, MutableMapping[Optional[str], Tool]] = None,
|
||||
in_prefix: str = 'in_',
|
||||
out_prefix: str = '',
|
||||
port_map: Optional[Union[Dict[str, str], Sequence[str]]] = None,
|
||||
@ -296,6 +299,8 @@ class PortList(metaclass=ABCMeta):
|
||||
`DeviceError` if applying the prefixes results in duplicate port
|
||||
names.
|
||||
"""
|
||||
from .pattern import Pattern
|
||||
|
||||
if port_map:
|
||||
if isinstance(port_map, dict):
|
||||
missing_inkeys = set(port_map.keys()) - set(self.ports.keys())
|
||||
@ -319,7 +324,7 @@ class PortList(metaclass=ABCMeta):
|
||||
if duplicates:
|
||||
raise DeviceError(f'Duplicate keys after prefixing, try a different prefix: {duplicates}')
|
||||
|
||||
new = Builder(library=library, ports={**ports_in, **ports_out}, tools=tools)
|
||||
new = Builder(library=library, pattern=Pattern(ports={**ports_in, **ports_out}), tools=tools)
|
||||
return new
|
||||
|
||||
def find_transform(
|
||||
|
@ -162,13 +162,13 @@ class Ref(
|
||||
|
||||
return pattern
|
||||
|
||||
def rotate(self: S, rotation: float) -> S:
|
||||
def rotate(self: R, rotation: float) -> R:
|
||||
self.rotation += rotation
|
||||
if self.repetition is not None:
|
||||
self.repetition.rotate(rotation)
|
||||
return self
|
||||
|
||||
def mirror(self: S, axis: int) -> S:
|
||||
def mirror(self: R, axis: int) -> R:
|
||||
self.mirrored[axis] = not self.mirrored[axis]
|
||||
self.rotation *= -1
|
||||
if self.repetition is not None:
|
||||
|
Loading…
Reference in New Issue
Block a user