Source code for qiskit_metal.qlibrary.tlines.framed_path

# -*- coding: utf-8 -*-

# This code is part of Qiskit.
#
# (C) Copyright IBM 2017, 2021.
#
# This code is licensed under the Apache License, Version 2.0. You may
# obtain a copy of this license in the LICENSE.txt file in the root directory
# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.
#
# Any modifications or derivative works of this code must retain this
# copyright notice, and modified files need to carry a notice indicating
# that they have been altered from the originals.

import numpy as np
from numpy.linalg import norm
from qiskit_metal import Dict
from qiskit_metal.qlibrary.core import QRoute
from qiskit_metal.toolbox_metal import math_and_overrides as mao


[docs] class RouteFramed(QRoute): """A non-meandered sample_shapes CPW that is auto-generated between 2 components. Designed to avoid self-collisions and collisions with components it is attached to. This class extends the `QComponent` class. .. meta:: Routed Frame Assumptions: 1. Components are situated along xy axes in 2 dimensions. No rotation is allowed (yet). Their bounding boxes may not overlap, though they may be situated at arbitrary x and y provided these conditions are met. 2. Pins point normal to qubits ("directly outward") and either in the x or y directions. They must not protrude from the exact corner of a component. [This last assumption has implications for 2-segment connections.] 3. Intersection of CPWs with themselves or the qubits they stem from is prohibited. Intersection with other components/CPWs has not yet been considered. 4. Components may not share an edge; a nonzero gap must be present between 2 adjacent qubits. 5. CPWs must be attached to protruding leads via terminations head-on, not from the sides. """ component_metadata = Dict(short_name='cpw') """Component metadata""" TOOLTIP = """A non-meandered basic CPW that is auto-generated between 2 components."""
[docs] def make(self): """Use user-specified parameters and geometric orientation of components to determine whether the CPW connecting the pins on either end requires 1, 2, or 3 segments. Preference given to shorter paths and paths that flow with the leadin and leadout directions rather than turning immediately at 90 deg. Keepout region along x and y directions specified for CPWs that wrap around outer perimeter of overall bounding box of both components. """ self.__pts = [] # list of 2D numpy arrays containing vertex locations p = self.p # parsed options w = p.trace_width leadstart = p.lead.start_straight leadend = p.lead.end_straight keepoutx = 0.2 keepouty = 0.2 # Set the CPW pins and add the points/directions to the lead-in/out arrays self.set_pin("start") self.set_pin("end") # Align the lead-in/out to the input options set from the user meander_start_point = self.set_lead("start") meander_end_point = self.set_lead("end") n1 = meander_start_point.direction n2 = meander_end_point.direction m1 = meander_start_point.position m2 = meander_end_point.position component_start = p.pin_inputs['start_pin']['component'] component_end = p.pin_inputs['end_pin']['component'] # Coordinates of bounding box for each individual component minx1, miny1, maxx1, maxy1 = self.design.components[ component_start].qgeometry_bounds() minx2, miny2, maxx2, maxy2 = self.design.components[ component_end].qgeometry_bounds() # Coordinates of overall bounding box which includes both components minx, miny = min(minx1, minx2), min(miny1, miny2) maxx, maxy = max(maxx1, maxx2), max(maxy1, maxy2) # Normdot is dot product of normal vectors normdot = mao.dot(n1, n2) if normdot == -1: # Modify CPW endpoints to include mandatory w / 2 leadin plus user defined leadin m1ext = m1 + n1 * (w / 2 + leadstart) m2ext = m2 + n2 * (w / 2 + leadend) # Alignment is between displacement of modified positions (see above) and normal vector alignment = mao.dot((m2ext - m1ext) / norm(m2ext - m1ext), n1) if alignment == 1: # Normal vectors point directly at each other; no obstacles in between # Connect with single segment; generalizes to arbitrary angles with no snapping self.__pts = self.connect_frame(1) elif alignment > 0: # Displacement partially aligned with normal vectors # Connect with antisymmetric 3-segment CPW if n1[1] == 0: # Normal vectors horizontal if minx2 < maxx1: self.__pts = self.connect_frame(3, 0, (minx2 + maxx1) / 2) else: self.__pts = self.connect_frame(3, 0, (minx1 + maxx2) / 2) else: # Normal vectors vertical if miny2 < maxy1: self.__pts = self.connect_frame(3, 1, (miny2 + maxy1) / 2) else: self.__pts = self.connect_frame(3, 1, (miny1 + maxy2) / 2) elif alignment == 0: # Displacement orthogonal to normal vectors # Connect with 1 segment self.__pts = self.connect_frame(1) elif alignment < 0: # Normal vectors on opposite sides of squares if n1[1] == 0: # Normal vectors horizontal if maxy1 < miny2: self.__pts = self.connect_frame(3, 1, (maxy1 + miny2) / 2) elif maxy2 < miny1: self.__pts = self.connect_frame(3, 1, (maxy2 + miny1) / 2) else: # Gap running only vertically -> must wrap around with shorter of 2 possibilities # pts_top represents 3-segment CPW running along top edge of overall bounding box # pts_bott represents 3-segment CPW running along bottom edge of overall bounding box pts_top = self.connect_frame(3, 1, maxy + keepouty) pts_bott = self.connect_frame(3, 1, miny - keepouty) if self.totlength(pts_top) < self.totlength(pts_bott): self.__pts = pts_top else: self.__pts = pts_bott else: # Normal vectors vertical if maxx1 < minx2: self.__pts = self.connect_frame(3, 0, (maxx1 + minx2) / 2) elif maxx2 < minx1: self.__pts = self.connect_frame(3, 0, (maxx2 + minx1) / 2) else: # Gap running only horizontally -> must wrap around with shorter of 2 possibilities # pts_left represents 3-segment CPW running along left edge of overall bounding box # pts_right represents 3-segment CPW running along right edge of overall bounding box pts_left = self.connect_frame(3, 0, minx - keepoutx) pts_right = self.connect_frame(3, 0, maxx + keepoutx) if self.totlength(pts_left) < self.totlength(pts_right): self.__pts = pts_left else: self.__pts = pts_right elif normdot == 0: # Normal vectors perpendicular to each other if (m1[0] in [minx, maxx]) and (m2[1] in [miny, maxy]): # Both pins on perimeter of overall bounding box, but not at corner self.__pts = self.connect_frame(2) elif (m1[1] in [miny, maxy]) and (m2[0] in [minx, maxx]): # Both pins on perimeter of overall bounding box, but not at corner self.__pts = self.connect_frame(2) elif (m1[0] not in [minx, maxx]) and (m2[0] not in [ minx, maxx ]) and (m1[1] not in [miny, maxy]) and (m2[1] not in [miny, maxy]): # Neither pin lies on perimeter of overall bounding box # Always possible to connect with at most 2 segments if (m1[0] == m2[0]) or (m1[1] == m2[1]): # Connect directly with 1 segment self.__pts = self.connect_frame(1) else: # Connect with 2 segments self.__pts = self.connect_frame(2) elif (m1[0] in [minx, maxx]) or (m1[1] in [miny, maxy]): # Pin 1 lies on boundary of overall bounding box but pin 2 does not if m1[0] in [minx, maxx]: # Pin 1 on left or right boundary, pointing left or right, respectively if n2[1] > 0: # Pin 2 pointing up if miny1 > maxy2: self.__pts = self.connect_frame(2) else: self.__pts = self.connect_frame( 3, 1, maxy + keepouty) else: # Pin 2 pointing down if miny2 > maxy1: self.__pts = self.connect_frame(2) else: self.__pts = self.connect_frame( 3, 1, miny - keepouty) elif m1[1] in [miny, maxy]: # Pin 1 on bottom or top boundary, pointing down or up, respectively if n2[0] < 0: # Pin 2 pointing left if minx2 > maxx1: self.__pts = self.connect_frame(2) else: self.__pts = self.connect_frame( 3, 0, minx - keepoutx) else: # Pin 2 pointing right if minx1 > maxx2: self.__pts = self.connect_frame(2) else: self.__pts = self.connect_frame( 3, 0, maxx + keepoutx) elif (m2[0] in [minx, maxx]) or (m2[1] in [miny, maxy]): # Pin 2 lies on boundary of overall bounding box but pin 1 does not if m2[0] in [minx, maxx]: # Pin 2 on left or right boundary, pointing left or right, respectively if n1[1] > 0: # Pin 1 pointing up if miny2 > maxy1: self.__pts = self.connect_frame(2) else: self.__pts = self.connect_frame( 3, 1, maxy + keepouty) else: # Pin 1 pointing down if miny1 > maxy2: self.__pts = self.connect_frame(2) else: self.__pts = self.connect_frame( 3, 1, miny - keepouty) elif m2[1] in [miny, maxy]: # Pin 2 on bottom or top boundary, pointing down or up, respectively if n1[0] < 0: # Pin 1 pointing left if minx1 > maxx2: self.__pts = self.connect_frame(2) else: self.__pts = self.connect_frame( 3, 0, minx - keepoutx) else: # Pin 1 pointing right if minx2 > maxx1: self.__pts = self.connect_frame(2) else: self.__pts = self.connect_frame( 3, 0, maxx + keepoutx) else: # Normal vectors pointing in same direction if ((m1[0] == m2[0]) or (m1[1] == m2[1])) and (mao.dot(n1, m2 - m1) == 0): # Connect directly with 1 segment self.__pts = self.connect_frame(1) elif n1[1] == 0: # Normal vectors horizontal if (m1[1] > maxy2) or (m2[1] > maxy1): # Connect with 2 segments self.__pts = self.connect_frame(2) else: # Must wrap around with shorter of the 2 following possibilities: # pts_top represents 3-segment CPW running along top edge of overall bounding box # pts_bott represents 3-segment CPW running along bottom edge of overall bounding box pts_top = self.connect_frame(3, 1, maxy + keepouty) pts_bott = self.connect_frame(3, 1, miny - keepouty) if self.totlength(pts_top) < self.totlength(pts_bott): self.__pts = pts_top else: self.__pts = pts_bott else: # Normal vectors vertical if (m1[0] > maxx2) or (m2[0] > maxx1): # Connect with 2 segments self.__pts = self.connect_frame(2) else: # Must wrap around with shorter of the 2 following possibilities: # pts_left represents 3-segment CPW running along left edge of overall bounding box # pts_right represents 3-segment CPW running along right edge of overall bounding box pts_left = self.connect_frame(3, 0, minx - keepoutx) pts_right = self.connect_frame(3, 0, maxx + keepoutx) if self.totlength(pts_left) < self.totlength(pts_right): self.__pts = pts_left else: self.__pts = pts_right self.intermediate_pts = self.__pts # Make points into elements self.make_elements(self.get_points())
[docs] def connect_frame(self, segments: int, constaxis=0, constval=0) -> list: """Generate the list of 2D coordinates comprising a CPW between startpin and endpin. Args: startpin (str): Name of startpin endpin (str): Name of endpin width (float): Width of CPW in mm segments (int): Number of segments in the CPW, not including leadin and leadout. Ranges from 1 to 3. leadstart (float): Length of first CPW segment originating at startpin leadend (float): Length of final CPW segment ending at endpin constaxis (int, optional): In the case of 3 segment CPWs, the constant axis of the line that both leadin and leadout must connect to. Example: If x = 3, the constant axis (x) is 0. Defaults to 0. constval (int, optional): In the case of 3 segment CPWs, the constant numerical value of the line that both leadin and leadout must connect to. Example: If x = 3, the constant value is 3. Defaults to 0. Returns: List: [np.array([x0, y0]), np.array([x1, y1]), np.array([x2, y2])] where xi, yi are vertices of CPW. """ start = self.head.get_tip() end = self.tail.get_tip() if segments == 1: # Straight across or up and down; no additional points necessary midcoords = [] elif segments == 2: # Choose between 2 diagonally opposing corners so that CPW doesn't trace back on itself corner1 = np.array([(start.position)[0], (end.position)[1]]) corner2 = np.array([(end.position)[0], (start.position)[1]]) startc1 = mao.dot(corner1 - (start.position), start.direction) endc1 = mao.dot(corner1 - (end.position), end.direction) startc2 = mao.dot(corner2 - (start.position), start.direction) endc2 = mao.dot(corner2 - (end.position), end.direction) # If both pins' normal vectors point towards one of the corners, pick that corner if (startc1 > 0) and (endc1 > 0): midcoords = [corner1] elif (startc2 > 0) and (endc2 > 0): midcoords = [corner2] elif min(startc1, endc1) >= 0: midcoords = [corner1] else: midcoords = [corner2] elif segments == 3: # Connect start and end to long segment at constaxis # 2 additional intermediate points needed to achieve this if constaxis == 0: # Middle segment lies on the line x = c for some constant c midcoord_start = np.array([constval, (start.position)[1]]) midcoord_end = np.array([constval, (end.position)[1]]) else: # Middle segment lies on the line y = d for some constant d midcoord_start = np.array([(start.position)[0], constval]) midcoord_end = np.array([(end.position)[0], constval]) midcoords = [midcoord_start, midcoord_end] startpts = [start.position] endpts = [end.position] return startpts + midcoords + endpts
[docs] def totlength(self, pts: list) -> float: """Get total length of all line segments in a given CPW.""" return sum(norm(pts[i] - pts[i - 1]) for i in range(1, len(pts)))