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