# -*- 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 qiskit_metal import Dict
from qiskit_metal.qlibrary.core import QRoutePoint
from collections import OrderedDict
from .meandered import RouteMeander
from .pathfinder import RoutePathfinder
# class RouteMixed(RouteFramed, RoutePathfinder, RouteMeander):
[docs]
class RouteMixed(RoutePathfinder, RouteMeander):
"""Implements fully featured Routing, allowing different type of
connections between anchors. The comprehensive Routing class. Inherits
`RoutePathfinder, RouteMeander` class, thus also QRoute and RouteAnchors.
.. meta::
Route Mixed
Default Options:
* between_anchors: Empty OrderedDict -- Intermediate anchors only; doesn't include endpoints
QRoute Default Options:
* pin_inputs: Dict
* start_pin: Dict -- Component and pin string pair. Define which pin to start from
* component: '' -- Name of component to start from, which has a pin
* pin: '' -- Name of pin used for pin_start
* end_pin=Dict -- Component and pin string pair. Define which pin to start from
* component: '' -- Name of component to end on, which has a pin
* pin: '' -- Name of pin used for pin_end
* lead: Dict
* start_straight: '0mm' -- Lead-in, defined as the straight segment extension from start_pin. Defaults to 0.1um.
* end_straight: '0mm' -- Lead-out, defined as the straight segment extension from end_pin. Defaults to 0.1um.
* start_jogged_extension: '' -- Lead-in, jogged extension of lead-in. Described as list of tuples
* end_jogged_extension: '' -- Lead-out, jogged extension of lead-out. Described as list of tuples
* fillet: '0'
* total_length: '7mm'
* trace_width: 'cpw_width' -- Defines the width of the line
RouteAnchors Default Options:
* anchors: OrderedDict -- Intermediate anchors only; doesn't include endpoints
RoutePathfinder Default Options:
* step_size: '0.25mm' -- Length of the step for the A* pathfinding algorithm
* advanced: Dict
* avoid_collision: 'true' -- true/false, defines if the route needs to avoid collisions. Defaults to 'true'.
RouteMeander Default Options:
* meander: Dict
* spacing: '200um' -- Minimum spacing between adjacent meander curves
* asymmetry='0um' -- Offset between the center-line of the meander and the center-line that stretches from the tip of lead-in to the x (or y) coordinate of the tip of the lead-out. Defaults to '0um'.
* snap: 'true'
* prevent_short_edges: 'true'
How to specify between_anchors for the RouteMixed
`between_anchors` have to be specified in an OrderedDict with incremental keys.
the value of each key specifies the type of routing algorythm to run between each anchors
and between the pins and the first and last anchors.
For example, for a RouteMixed with 4 anchors:
.. code-block:: python
:linenos:
between_anchors = OrderedDict()
between_anchors[0] = "S"
between_anchors[1] = "M"
between_anchors[2] = "S"
between_anchors[3] = "M"
between_anchors[4] = "PF"
* "S" = Utilizes the RouteStraight methods
* "M" = Utilizes the RouteMeander methods
* "PF" = Utilizes the RoutePathfinder methods
"""
default_options = Dict(
between_anchors=OrderedDict(
), # Intermediate anchors only; doesn't include endpoints
# Example: {1: "M", 2: "S", 3: "PF"}
# startpin -> startpin + leadin -> anchors -> endpin + leadout -> endpin
)
"""Default options"""
TOOLTIP = """Implements fully featured Routing, allowing different type of
connections between anchors."""
[docs]
def make(self):
"""Generates path from start pin to end pin."""
p = self.parse_options()
anchors = p.anchors
between_anchors = p.between_anchors
# 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
start_point = self.set_lead("start")
end_point = self.set_lead("end")
# approximate length needed for individual meanders
# the meander algorithm directly reads from self._length_segment
count_meanders_list = [
1 if x == "M" else 0 for x in list(between_anchors.values())
]
self._length_segment = None
if any(count_meanders_list):
self._length_segment = ((self.p.total_length - (self.head.length + self.tail.length) \
- self.free_manhattan_length_anchors()) / sum(count_meanders_list)) \
+ (self.free_manhattan_length_anchors() / len(count_meanders_list))
# find the points to connect between each pair of anchors, or between anchors and leads
# at first, store points "per segment" in a dictionary, so it is easier to apply length requirements
self.intermediate_pts = OrderedDict()
meanders = set()
for arc_num, coord in anchors.items():
# determine what is the connection strategy for this pair, based on user inputs
connect_method = self.select_connect_method(arc_num)
if connect_method == self.connect_meandered:
meanders.add(arc_num)
# compute points connecting the anchors, all but the last
arc_pts = connect_method(self.get_tip(), QRoutePoint(coord))
if arc_pts is None:
self.intermediate_pts[arc_num] = [coord]
else:
self.intermediate_pts[arc_num] = np.concatenate(
[arc_pts, [coord]], axis=0)
# compute last connection point to the output QRouteLead
connect_method = self.select_connect_method(len(anchors))
if connect_method == self.connect_meandered:
meanders.add(len(anchors))
arc_pts = connect_method(self.get_tip(), end_point)
if arc_pts is not None:
self.intermediate_pts[len(anchors)] = np.array(arc_pts)
# concatenate all points, transforming the dictionary into a single numpy array
self.trim_pts()
dictionary_intermediate_pts = self.intermediate_pts
self.intermediate_pts = np.concatenate(list(
self.intermediate_pts.values()),
axis=0)
if any(count_meanders_list):
# refine length of meanders
total_delta_length = self.p.total_length - self.length
individual_delta_length = total_delta_length / len(meanders)
for m in meanders:
arc_pts = dictionary_intermediate_pts[m][:-1]
if m == 0:
meander_start_point = start_point
else:
meander_start_point = QRoutePoint(anchors[m - 1])
if m == len(anchors):
meander_end_point = end_point
else:
meander_end_point = QRoutePoint(anchors[m])
dictionary_intermediate_pts[m] = self.adjust_length(
individual_delta_length, arc_pts, meander_start_point,
meander_end_point)
dictionary_intermediate_pts[m] = np.concatenate(
[dictionary_intermediate_pts[m], [anchors[m]]], axis=0)
self.intermediate_pts = np.concatenate(list(
dictionary_intermediate_pts.values()),
axis=0)
# Make points into elements
self.make_elements(self.get_points())
[docs]
def select_connect_method(self, segment_num):
"""Translates the user-selected connection method into the right method
to execute.
Args:
segment_num (int): Segment ID. Counts 0 as the first segment after the lead-in.
Return:
object: selected method
"""
between_anchors = self.parse_options().between_anchors
if segment_num in between_anchors:
type_connect = between_anchors[segment_num]
else:
type_connect = "S"
# print(type_connect)
if type_connect == "S":
return self.connect_simple
if type_connect == "PF":
return self.connect_astar_or_simple
if type_connect == "M":
return self.connect_meandered