Source code for qiskit_metal.designs.interface_components

# -*- 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.
"""Module containing Design interface components."""
from copy import deepcopy
from typing import TYPE_CHECKING, List, Union
#from typing import Any, Optional, TypeVar, Dict as Dict_, Iterable
from qiskit_metal.qlibrary.core._parsed_dynamic_attrs import is_ipython_magic
from qiskit_metal import logger
from qiskit_metal import config

if not config.is_building_docs():
    # Only import QComponent if the docs are NOT being built
    from ..qlibrary.core import QComponent

if TYPE_CHECKING:
    # For linting typechecking, import modules that can't be loaded here under normal conditions.
    # For example, I can't import QDesign, because it requires QComponent first. We have the
    # chicken and egg issue.
    from .design_base import QDesign


[docs] class Components: """This is a user interface for the design._components dict. The keys are unique integers, however, this interface allows user to treat the keys as strings. """ def __init__(self, design: 'QDesign'): """Set up variables and logger which are used to emulate a dict which is referencing design._components. Args: design (QDesign): Need to have a Qdesign class so this class can reference design._components. """ self._design = design self.logger = logger # type: logging.Logger self.components = design._components self.name_list = list() self.name_list_idx = 0
[docs] def __len__(self) -> int: """Give number of components in design. Returns: int: Total number of components registered within the design. """ return len(self.components)
[docs] def get_list_ints(self, component_names: List[str]) -> List[int]: """Provide corresponding ints to be used as keys for dict: design._components, when list of names is provided. Args: component_names (List[str]): Names of components which user wants to know the int to be used as a key. Returns: List[int]: Corresponding ints that user can use as keys into design._components """ component_ints = [self.find_id(item) for item in component_names] return component_ints
[docs] def find_id(self, name: str, quiet: bool = False) -> int: """Find id of component. The id is the key for a dict which holds all of the components within design. Args: name (str): Text name of component. The name is assumed to be unique. quiet (bool): Allow warning messages to be generated. Returns: int: Key to use in _components. If 0 is returned it means the name is not in dict. Raises: AttributeError: The given name is a magic method not in the dictionary """ if name in self._design.name_to_id: component_id = self._design.name_to_id[name] return component_id if not is_ipython_magic(name): # Name not registered, not in cache for components' names. # IPython checking methods # https://github.com/jupyter/notebook/issues/2014 if not quiet: self.logger.warning( f'In Components.find_id(), the name={name} is not used in design._components' ) return 0 raise AttributeError(name)
# def is_name_used(self, new_name: str) -> int: # """Check to see if name being used in components. # Args: # new_name (str): name to check # # Returns: # int: If the name does not exist, 0 is returned, otherwise the # component-id of component which is already using the name. # """ # all_names = [(value.name, key) # for (key, value) in self.components.items()] # search_result = [ # item for item in all_names if new_name == item[0]] # if len(search_result) != 0: # self.logger.warning( # f'Called interface_components, ' # f' component_id({search_result[0][0]}, id={search_result[0][1]})' # f' is already using name={new_name}.') # return search_result[0][1] # else: # return 0
[docs] def __getitem__(self, name: str, quiet: bool = False) -> Union[None, 'QComponent']: """Get the QComponent based on string name vs the unique id of QComponent. Args: name (str): Name of component. quiet (bool): Allow warning messages to be generated. Returns: QComponent: Class which describes the component. None if name not found in design._components. Raises: AttributeError: The given name is a magic method not in the dictionary """ component_id = int(self.find_id(name)) if component_id: return self._design._components[component_id] else: # IPython checking methods # https://github.com/jupyter/notebook/issues/2014 if not is_ipython_magic(name): if not quiet: self.logger.warning( f'In Components.__getitem__, name={name} is not ' f'registered in the design class. Return ' f'None for QComponent.') return None else: raise AttributeError(name) return None raise AttributeError(name)
def __setitem__(self, name: str, value: 'QComponent'): """Replace QComponent for an existing name. Use this at your own risk. There are netids used for pins within a component. The netids are used in the net_info table and qgeometry tables. Args: name (str): Name of QComponent. If not in design._components, then will be added to dict. If in dict, the value(QComponent) will replace existing QComponent. value (QComponent): QComponent to add under the given name """ if not isinstance(value, QComponent): self.logger.warning( f'The value is NOT a QComponent. Nothing has been assigned to name={name}' ) return component_id = self.find_id(name) if component_id: self.logger.debug( f'The name={name} already exists in design._components. ' f'A component_id={component_id} will be replaced.') self.components[component_id] = deepcopy(value) else: self.logger.warning( f'Usualy new components are added to design during init. ' f'The name={name} is not in design._components, and added as a new component.' ) value.name = name value._add_to_design() def __getattr__(self, name: str) -> Union['QComponent', None]: """Provide same behavior as __getitem__. Args: name (str): Name of component used to find the QComponent in design._components dict, vs using unique int id. Returns: QComponent: Class which describes the component. None if name not found in design._components. """ quiet = True return self.__getitem__(name, quiet) # def __setattr__(self, name: str, value: 'QComponent'): # """Provide same behavior as __setitem__. # Args: # name (str): Name of component used to find the QComponent in # design._components dict, vs using unique int id. # value (QComponent): Component with the name used in arguments. # """ # pass # #self.__setitem__(name, value) def __contains__(self, item: str) -> int: """Look for item in design._components in the value. Args: item (str): Name in the value of design._components. Returns: int: 0 if item is not in design._components.name otherwise an int which can be used as key for design._components[] """ if not isinstance(item, str): # self.logger.debug(f'Search with string in __contains__ {item}.') return 0 quiet = True return self.find_id(item, quiet) # def __delitem__(self, name: str): # """Will delete component from design class along with deleting the # net_info and element tables. # Args: # name (str): Name of component to delete from design._components. # """ # component_id = self.is_name_used(name) # self._design.delete_component(component_id) def __repr__(self) -> str: """Print the design._component dict. Returns: str : String to print design._component dict. """ return str(self._design._components.__repr__()) # # def __repr__(self): # # # make sure to define representation for print purpose # # # Why every class needs one? https://dbader.org/blog/python-repr-vs-str # # return str(self.__actual_place_I_store_components__.give_me_repr()) def __dir__(self) -> List: """Provide all the names in design._components. Returns: list: List of all the names used in design._components. """ all_names = [(value.name) for (key, value) in self.components.items()] return all_names # def __dir__(self): # # For autocompletion # return list(self.__get_dict__().keys()) def __iter__(self) -> iter: """Give iterator for design._components. Returns: iter: for design._components , the keys are names of the components. """ for value in self._design._components.values(): new_key = value.name yield new_key
[docs] def items(self) -> list: """Get a list of all the items. Returns: list: List of (key, value) pairs. """ all_items = [ (value.name, value) for (key, value) in self.components.items() ] return all_items
[docs] def values(self) -> list: """Get the list of all the values. Returns: list: List of just the values. """ all_items = [value for (key, value) in self.components.items()] return all_items
[docs] def keys(self) -> list: """Get the list of all the keys. Returns: list: List of just the keys. """ all_items = [value.name for (key, value) in self.components.items()] return all_items
# #### Down the line for serialization and pickling. Skip for now # def __getstate__(self): # """ # Serialize the object. # """ # # something like # return self.__actual_place_I_store_components__.__getstate__() # def __setstate__(self, state): # """ # Deserialize the object. # """ # pass