Full Code of ucb-art/BAG_framework for AI

master daf4b0aaa72f cached
162 files
1.4 MB
339.6k tokens
1515 symbols
1 requests
Download .txt
Showing preview only (1,488K chars total). Download the full file or copy to clipboard to get everything.
Repository: ucb-art/BAG_framework
Branch: master
Commit: daf4b0aaa72f
Files: 162
Total size: 1.4 MB

Directory structure:
gitextract_e9aig5le/

├── .gitignore
├── .gitmodules
├── LICENSE
├── README.md
├── bag/
│   ├── LICENSE
│   ├── __init__.py
│   ├── concurrent/
│   │   ├── LICENSE
│   │   ├── __init__.py
│   │   └── core.py
│   ├── core.py
│   ├── data/
│   │   ├── LICENSE
│   │   ├── __init__.py
│   │   ├── core.py
│   │   ├── dc.py
│   │   ├── digital.py
│   │   ├── lti.py
│   │   ├── ltv.py
│   │   ├── mos.py
│   │   └── plot.py
│   ├── design/
│   │   ├── LICENSE
│   │   ├── __init__.py
│   │   └── module.py
│   ├── interface/
│   │   ├── LICENSE
│   │   ├── __init__.py
│   │   ├── base.py
│   │   ├── database.py
│   │   ├── ocean.py
│   │   ├── server.py
│   │   ├── simulator.py
│   │   ├── skill.py
│   │   ├── templates/
│   │   │   ├── LICENSE
│   │   │   ├── Module.pyi
│   │   │   ├── PrimModule.pyi
│   │   │   ├── calibreview_setup.txt
│   │   │   ├── load_results.ocn
│   │   │   └── run_simulation.ocn
│   │   └── zmqwrapper.py
│   ├── io/
│   │   ├── LICENSE
│   │   ├── __init__.py
│   │   ├── common.py
│   │   ├── file.py
│   │   ├── gui.py
│   │   ├── process.py
│   │   ├── sim_data.py
│   │   └── template.py
│   ├── layout/
│   │   ├── LICENSE
│   │   ├── __init__.py
│   │   ├── core.py
│   │   ├── digital.py
│   │   ├── objects.py
│   │   ├── routing/
│   │   │   ├── LICENSE
│   │   │   ├── __init__.py
│   │   │   ├── base.py
│   │   │   ├── fill.py
│   │   │   └── grid.py
│   │   ├── tech.py
│   │   ├── template.py
│   │   └── util.py
│   ├── math/
│   │   ├── LICENSE
│   │   ├── __init__.py
│   │   ├── dfun.py
│   │   └── interpolate.py
│   ├── mdao/
│   │   ├── LICENSE
│   │   ├── __init__.py
│   │   ├── components.py
│   │   └── core.py
│   ├── simulation/
│   │   ├── LICENSE
│   │   ├── __init__.py
│   │   ├── core.py
│   │   └── core_v2.py
│   ├── tech/
│   │   ├── LICENSE
│   │   ├── __init__.py
│   │   ├── core.py
│   │   └── mos.py
│   ├── util/
│   │   ├── LICENSE
│   │   ├── __init__.py
│   │   ├── cache.py
│   │   ├── immutable.py
│   │   ├── interval.py
│   │   ├── parse.py
│   │   └── search.py
│   ├── verification/
│   │   ├── LICENSE
│   │   ├── __init__.py
│   │   ├── base.py
│   │   ├── calibre.py
│   │   ├── icv.py
│   │   ├── pvs.py
│   │   ├── templates/
│   │   │   ├── LICENSE
│   │   │   ├── layout_export_config.txt
│   │   │   └── si_env.txt
│   │   └── virtuoso.py
│   └── virtuoso.py
├── docs/
│   ├── .gitignore
│   ├── LICENSE
│   ├── Makefile
│   ├── README
│   ├── refresh_api.sh
│   └── source/
│       ├── LICENSE
│       ├── api/
│       │   ├── LICENSE
│       │   ├── bag.data.rst
│       │   ├── bag.design.rst
│       │   ├── bag.interface.rst
│       │   ├── bag.io.rst
│       │   ├── bag.layout.routing.rst
│       │   ├── bag.layout.rst
│       │   ├── bag.math.rst
│       │   ├── bag.mdao.rst
│       │   ├── bag.rst
│       │   ├── bag.tech.rst
│       │   ├── bag.util.rst
│       │   ├── bag.verification.rst
│       │   └── modules.rst
│       ├── conf.py
│       ├── developer/
│       │   ├── LICENSE
│       │   └── developer.rst
│       ├── index.rst
│       ├── overview/
│       │   ├── LICENSE
│       │   ├── design.rst
│       │   ├── overview.rst
│       │   ├── schematic.rst
│       │   └── testbench.rst
│       ├── setup/
│       │   ├── LICENSE
│       │   ├── bag_config/
│       │   │   ├── LICENSE
│       │   │   ├── bag_config.rst
│       │   │   ├── database/
│       │   │   │   └── database.rst
│       │   │   ├── misc.rst
│       │   │   ├── simulation/
│       │   │   │   └── simulation.rst
│       │   │   └── socket/
│       │   │       └── socket.rst
│       │   ├── config_summary.rst
│       │   ├── install_python.rst
│       │   ├── new_pdk.rst
│       │   ├── pyoptsparse.rst
│       │   ├── setup.rst
│       │   └── tech_config/
│       │       ├── LICENSE
│       │       ├── layout/
│       │       │   └── layout.rst
│       │       ├── misc.rst
│       │       ├── mos/
│       │       │   └── mos.rst
│       │       └── tech_config.rst
│       └── tutorial/
│           ├── LICENSE
│           ├── figures/
│           │   └── LICENSE
│           └── tutorial.rst
├── run_scripts/
│   ├── LICENSE
│   ├── clean_cds_lib.py
│   ├── compile_verilog.il
│   ├── gen_cell.py
│   ├── generate_verilog.py
│   ├── meas_cell.py
│   ├── run_bag.sh
│   ├── setup_submodules.py
│   ├── sim_cell.py
│   ├── start_bag.il
│   ├── start_bag.sh
│   ├── start_bag_ICADV12d3.il
│   └── virt_server.sh
├── setup.py
└── tests/
    ├── LICENSE
    ├── __init__.py
    └── layout/
        ├── LICENSE
        ├── __init__.py
        └── routing/
            ├── LICENSE
            ├── __init__.py
            └── test_fill.py

================================================
FILE CONTENTS
================================================

================================================
FILE: .gitignore
================================================
*~
*.pyc
.idea
build
dist
bag.egg-info
__pycache__
*.swp


================================================
FILE: .gitmodules
================================================
[submodule "cybag_oa"]
	path = cybag_oa
    url = https://github.com/ucb-art/cybag_oa.git


================================================
FILE: LICENSE
================================================
BSD 3-Clause License

Copyright (c) 2018, Regents of the University of California
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

* Redistributions of source code must retain the above copyright notice, this
  list of conditions and the following disclaimer.

* Redistributions in binary form must reproduce the above copyright notice,
  this list of conditions and the following disclaimer in the documentation
  and/or other materials provided with the distribution.

* Neither the name of the copyright holder nor the names of its
  contributors may be used to endorse or promote products derived from
  this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.


================================================
FILE: README.md
================================================
Berkeley Analog Generator (BAG) version 2.0 and later.

BAG 2.0 is a complete rewrite of BAG 1.x (which is in pre-alpha stage and
never released publicly).

(Very outdated) Documentation and install instructions can be found at <http://bag-framework.readthedocs.io/en/latest/>

A tutorial setup is available at <https://github.com/ucb-art/BAG2_cds_ff_mpt.git/>


================================================
FILE: bag/LICENSE
================================================
BSD 3-Clause License

Copyright (c) 2018, Regents of the University of California
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

* Redistributions of source code must retain the above copyright notice, this
  list of conditions and the following disclaimer.

* Redistributions in binary form must reproduce the above copyright notice,
  this list of conditions and the following disclaimer in the documentation
  and/or other materials provided with the distribution.

* Neither the name of the copyright holder nor the names of its
  contributors may be used to endorse or promote products derived from
  this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.


================================================
FILE: bag/__init__.py
================================================
# -*- coding: utf-8 -*-

"""This is the bag root package.
"""

import signal

from . import math
from .math import float_to_si_string, si_string_to_float
from . import interface
from . import design
from . import data
from . import tech
from . import layout

from .core import BagProject, create_tech_info

__all__ = ['interface', 'design', 'data', 'math', 'tech', 'layout', 'BagProject',
           'float_to_si_string', 'si_string_to_float', 'create_tech_info']

# make sure that SIGINT will always be catched by python.
signal.signal(signal.SIGINT, signal.default_int_handler)


================================================
FILE: bag/concurrent/LICENSE
================================================
BSD 3-Clause License

Copyright (c) 2018, Regents of the University of California
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

* Redistributions of source code must retain the above copyright notice, this
  list of conditions and the following disclaimer.

* Redistributions in binary form must reproduce the above copyright notice,
  this list of conditions and the following disclaimer in the documentation
  and/or other materials provided with the distribution.

* Neither the name of the copyright holder nor the names of its
  contributors may be used to endorse or promote products derived from
  this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.


================================================
FILE: bag/concurrent/__init__.py
================================================
# -*- coding: utf-8 -*-

"""This package define helper classes used to perform concurrent operations.
"""

================================================
FILE: bag/concurrent/core.py
================================================
# -*- coding: utf-8 -*-

"""This module define utility classes for performing concurrent operations.
"""

from typing import Optional, Sequence, Dict, Union, Tuple, Callable, Any

import os
import asyncio
# noinspection PyProtectedMember
from asyncio.subprocess import Process
import subprocess
import multiprocessing
from concurrent.futures import CancelledError


def batch_async_task(coro_list):
    """Execute a list of coroutines or futures concurrently.

    User may press Ctrl-C to cancel all given tasks.

    Parameters
    ----------
    coro_list :
        a list of coroutines or futures to run concurrently.

    Returns
    -------
    results :
        a list of return values or raised exceptions of given tasks.
    """
    top_future = asyncio.gather(*coro_list, return_exceptions=True)

    loop = asyncio.get_event_loop()
    try:
        print('Running tasks, Press Ctrl-C to cancel.')
        results = loop.run_until_complete(top_future)
    except KeyboardInterrupt:
        print('Ctrl-C detected, Cancelling tasks.')
        top_future.cancel()
        loop.run_forever()
        results = None

    return results


ProcInfo = Tuple[Union[str, Sequence[str]], str, Optional[Dict[str, str]], Optional[str]]
FlowInfo = Tuple[Union[str, Sequence[str]], str, Optional[Dict[str, str]], Optional[str],
                 Callable[[Optional[int], str], Any]]


class SubProcessManager(object):
    """A class that provides convenient methods to run multiple subprocesses in parallel using asyncio.

    Parameters
    ----------
    max_workers : Optional[int]
        number of maximum allowed subprocesses.  If None, defaults to system
        CPU count.
    cancel_timeout : Optional[float]
        Number of seconds to wait for a process to terminate once SIGTERM or
        SIGKILL is issued.  Defaults to 10 seconds.
    """

    def __init__(self, max_workers=None, cancel_timeout=10.0):
        # type: (Optional[int], Optional[float]) -> None
        if max_workers is None:
            max_workers = multiprocessing.cpu_count()
        if cancel_timeout is None:
            cancel_timeout = 10.0

        self._cancel_timeout = cancel_timeout
        self._semaphore = asyncio.Semaphore(max_workers)

    async def _kill_subprocess(self, proc: Optional[Process]) -> None:
        """Helper method; send SIGTERM/SIGKILL to a subprocess.

        This method first sends SIGTERM to the subprocess.  If the process hasn't terminated
        after a given timeout, it sends SIGKILL.

        Parameter
        ---------
        proc : Optional[Process]
            the process to attempt to terminate.  If None, this method does nothing.
        """
        if proc is not None:
            if proc.returncode is None:
                try:
                    proc.terminate()
                    try:
                        await asyncio.shield(asyncio.wait_for(proc.wait(), self._cancel_timeout))
                    except CancelledError:
                        pass

                    if proc.returncode is None:
                        proc.kill()
                        try:
                            await asyncio.shield(
                                asyncio.wait_for(proc.wait(), self._cancel_timeout))
                        except CancelledError:
                            pass
                except ProcessLookupError:
                    pass

    async def async_new_subprocess(self,
                                   args: Union[str, Sequence[str]],
                                   log: str,
                                   env: Optional[Dict[str, str]] = None,
                                   cwd: Optional[str] = None) -> Optional[int]:
        """A coroutine which starts a subprocess.

        If this coroutine is cancelled, it will shut down the subprocess gracefully using
        SIGTERM/SIGKILL, then raise CancelledError.

        Parameters
        ----------
        args : Union[str, Sequence[str]]
            command to run, as string or sequence of strings.
        log : str
            the log file name.
        env : Optional[Dict[str, str]]
            an optional dictionary of environment variables.  None to inherit from parent.
        cwd : Optional[str]
            the working directory.  None to inherit from parent.

        Returns
        -------
        retcode : Optional[int]
            the return code of the subprocess.
        """
        if isinstance(args, str):
            args = [args]

        # get log file name, make directory if necessary
        log = os.path.abspath(log)
        if os.path.isdir(log):
            raise ValueError('log file %s is a directory.' % log)
        os.makedirs(os.path.dirname(log), exist_ok=True)

        async with self._semaphore:
            proc = None
            with open(log, 'w') as logf:
                logf.write('command: %s\n' % (' '.join(args)))
                logf.flush()
                try:
                    proc = await asyncio.create_subprocess_exec(*args, stdout=logf,
                                                                stderr=subprocess.STDOUT,
                                                                env=env, cwd=cwd)
                    retcode = await proc.wait()
                    return retcode
                except CancelledError as err:
                    await self._kill_subprocess(proc)
                    raise err

    async def async_new_subprocess_flow(self,
                                        proc_info_list: Sequence[FlowInfo]) -> Any:
        """A coroutine which runs a series of subprocesses.

        If this coroutine is cancelled, it will shut down the current subprocess gracefully using
        SIGTERM/SIGKILL, then raise CancelledError.

        Parameters
        ----------
        proc_info_list : Sequence[FlowInfo]
            a list of processes to execute in series.  Each element is a tuple of:

            args : Union[str, Sequence[str]]
                command to run, as string or list of string arguments.
            log : str
                log file name.
            env : Optional[Dict[str, str]]
                environment variable dictionary.  None to inherit from parent.
            cwd : Optional[str]
                working directory path.  None to inherit from parent.
            vfun : Sequence[Callable[[Optional[int], str], Any]]
                a function to validate if it is ok to execute the next process.  The output of the
                last function is returned.  The first argument is the return code, the second
                argument is the log file name.

        Returns
        -------
        result : Any
            the return value of the last validate function.  None if validate function
            returns False.
        """
        num_proc = len(proc_info_list)
        if num_proc == 0:
            return None

        async with self._semaphore:
            for idx, (args, log, env, cwd, vfun) in enumerate(proc_info_list):
                if isinstance(args, str):
                    args = [args]

                # get log file name, make directory if necessary
                log = os.path.abspath(log)
                if os.path.isdir(log):
                    raise ValueError('log file %s is a directory.' % log)
                os.makedirs(os.path.dirname(log), exist_ok=True)

                proc, retcode = None, None
                with open(log, 'w') as logf:
                    logf.write('command: %s\n' % (' '.join(args)))
                    logf.flush()
                    try:
                        proc = await asyncio.create_subprocess_exec(*args, stdout=logf,
                                                                    stderr=subprocess.STDOUT,
                                                                    env=env, cwd=cwd)
                        retcode = await proc.wait()
                    except CancelledError as err:
                        await self._kill_subprocess(proc)
                        raise err

                fun_output = vfun(retcode, log)
                if idx == num_proc - 1:
                    return fun_output
                elif not fun_output:
                    return None

    def batch_subprocess(self, proc_info_list):
        # type: (Sequence[ProcInfo]) -> Optional[Sequence[Union[int, Exception]]]
        """Run all given subprocesses in parallel.

        Parameters
        ----------
        proc_info_list : Sequence[ProcInfo]
            a list of process information.  Each element is a tuple of:

            args : Union[str, Sequence[str]]
                command to run, as string or list of string arguments.
            log : str
                log file name.
            env : Optional[Dict[str, str]]
                environment variable dictionary.  None to inherit from parent.
            cwd : Optional[str]
                working directory path.  None to inherit from parent.

        Returns
        -------
        results : Optional[Sequence[Union[int, Exception]]]
            if user cancelled the subprocesses, None is returned.  Otherwise, a list of
            subprocess return codes or exceptions are returned.
        """
        num_proc = len(proc_info_list)
        if num_proc == 0:
            return []

        coro_list = [self.async_new_subprocess(args, log, env, cwd) for args, log, env, cwd in
                     proc_info_list]

        return batch_async_task(coro_list)

    def batch_subprocess_flow(self, proc_info_list):
        # type: (Sequence[Sequence[FlowInfo]]) -> Optional[Sequence[Union[int, Exception]]]
        """Run all given subprocesses flow in parallel.

        Parameters
        ----------
        proc_info_list : Sequence[Sequence[FlowInfo]
            a list of process flow information.  Each element is a sequence of tuples of:

            args : Union[str, Sequence[str]]
                command to run, as string or list of string arguments.
            log : str
                log file name.
            env : Optional[Dict[str, str]]
                environment variable dictionary.  None to inherit from parent.
            cwd : Optional[str]
                working directory path.  None to inherit from parent.
            vfun : Sequence[Callable[[Optional[int], str], Any]]
                a function to validate if it is ok to execute the next process.  The output of the
                last function is returned.  The first argument is the return code, the second
                argument is the log file name.

        Returns
        -------
        results : Optional[Sequence[Any]]
            if user cancelled the subprocess flows, None is returned.  Otherwise, a list of
            flow return values or exceptions are returned.
        """
        num_proc = len(proc_info_list)
        if num_proc == 0:
            return []

        coro_list = [self.async_new_subprocess_flow(flow_info) for flow_info in proc_info_list]

        return batch_async_task(coro_list)


================================================
FILE: bag/core.py
================================================
# -*- coding: utf-8 -*-

"""This is the core bag module.
"""

from typing import TYPE_CHECKING, Dict, Any, Tuple, Optional, Union, Type, Sequence, TypeVar

import os
import importlib
import cProfile
import pstats
from pathlib import Path

# noinspection PyPackageRequirements

from .interface import ZMQDealer
from .interface.database import DbAccess
from .design import ModuleDB, SchInstance
from .layout.routing import RoutingGrid
from .layout.template import TemplateDB
from .layout.core import DummyTechInfo
from .io import read_file, sim_data, read_yaml_env
from .concurrent.core import batch_async_task

if TYPE_CHECKING:
    from .interface.simulator import SimAccess
    from .layout.template import TemplateBase
    from .layout.core import TechInfo
    from .design.module import Module
    from .simulation.core_v2 import TestbenchManager, MeasurementManager

    ModuleType = TypeVar('ModuleType', bound=Module)
    TemplateType = TypeVar('TemplateType', bound=TemplateBase)


def _get_config_file_abspath(fname):
    """Get absolute path of configuration file using BAG_WORK_DIR environment variable."""
    fname = os.path.basename(fname)
    if 'BAG_WORK_DIR' not in os.environ:
        raise ValueError('Environment variable BAG_WORK_DIR not defined')

    work_dir = os.environ['BAG_WORK_DIR']
    if not os.path.isdir(work_dir):
        raise ValueError('$BAG_WORK_DIR = %s is not a directory' % work_dir)

    # read port number
    fname = os.path.join(work_dir, fname)
    if not os.path.isfile(fname):
        raise ValueError('Cannot find file: %s' % fname)
    return fname


def _get_port_number(port_file):
    # type: (str) -> Tuple[Optional[int], str]
    """Read the port number from the given port file.

    Parameters
    ----------
    port_file : str
        a file containing the communication port number.

    Returns
    -------
    port : Optional[int]
        the port number if reading is successful.
    msg : str
        Empty string on success, the error message on failure.
    """
    try:
        port_file = _get_config_file_abspath(port_file)
    except ValueError as err:
        return None, str(err)

    port = int(read_file(port_file))
    return port, ''


def _import_class_from_str(class_str):
    # type: (str) -> Type
    """Given a Python class string, convert it to the Python class.

    Parameters
    ----------
    class_str : str
        a Python class string/

    Returns
    -------
    py_class : class
        a Python class.
    """
    sections = class_str.split('.')

    module_str = '.'.join(sections[:-1])
    class_str = sections[-1]
    modul = importlib.import_module(module_str)
    return getattr(modul, class_str)


class Testbench(object):
    """A class that represents a testbench instance.

    Parameters
    ----------
    sim : :class:`bag.interface.simulator.SimAccess`
        The SimAccess instance used to issue simulation commands.
    db : :class:`bag.interface.database.DbAccess`
        The DbAccess instance used to update testbench schematic.
    lib : str
        testbench library.
    cell : str
        testbench cell.
    parameters : Dict[str, str]
        the simulation parameter dictionary.  The values are string representation
        of actual parameter values.
    env_list : Sequence[str]
        list of defined simulation environments.
    default_envs : Sequence[str]
        the selected simulation environments.
    outputs : Dict[str, str]
        default output expressions

    Attributes
    ----------
    lib : str
        testbench library.
    cell : str
        testbench cell.
    save_dir : str
        directory containing the last simulation data.
    """

    def __init__(self,  # type: Testbench
                 sim,  # type: SimAccess
                 db,  # type: DbAccess
                 lib,  # type: str
                 cell,  # type: str
                 parameters,  # type: Dict[str, str]
                 env_list,  # type: Sequence[str]
                 default_envs,  # type: Sequence[str]
                 outputs,  # type: Dict[str, str]
                 ):
        # type: (...) -> None
        """Create a new testbench instance.
        """
        self.sim = sim
        self.db = db
        self.lib = lib
        self.cell = cell
        self.parameters = parameters
        self.env_parameters = {}
        self.env_list = env_list
        self.sim_envs = default_envs
        self.config_rules = {}
        self.outputs = outputs
        self.save_dir = None

    def get_defined_simulation_environments(self):
        # type: () -> Sequence[str]
        """Return a list of defined simulation environments"""
        return self.env_list

    def get_current_simulation_environments(self):
        # type: () -> Sequence[str]
        """Returns a list of simulation environments this testbench will simulate."""
        return self.sim_envs

    def add_output(self, var, expr):
        # type: (str, str) -> None
        """Add an output expression to be recorded and exported back to python.

        Parameters
        ----------
        var : str
            output variable name.
        expr : str
            the output expression.
        """
        if var in sim_data.illegal_var_name:
            raise ValueError('Variable name %s is illegal.' % var)
        self.outputs[var] = expr

    def set_parameter(self, name, val, precision=6):
        # type: (str, Union[int, float], int) -> None
        """Sets the value of the given simulation parameter.

        Parameters
        ----------
        name : str
            parameter name.
        val : Union[int, float]
            parameter value
        precision : int
            the parameter value will be rounded to this precision.
        """
        param_config = dict(type='single', value=val)
        if isinstance(val, str):
            self.parameters[name] = val
        else:
            self.parameters[name] = self.sim.format_parameter_value(param_config, precision)

    def set_env_parameter(self, name, val_list, precision=6):
        # type: (str, Sequence[float], int) -> None
        """Configure the given parameter to have different value across simulation environments.

        Parameters
        ----------
        name : str
            the parameter name.
        val_list : Sequence[float]
            the parameter values for each simulation environment.  the order of the simulation
            environments can be found in self.sim_envs
        precision : int
            the parameter value will be rounded to this precision.
        """
        if len(self.sim_envs) != len(val_list):
            raise ValueError('env parameter must have %d values.' % len(self.sim_envs))

        default_val = None
        for env, val in zip(self.sim_envs, val_list):
            if env not in self.env_parameters:
                cur_dict = {}
                self.env_parameters[env] = cur_dict
            else:
                cur_dict = self.env_parameters[env]

            param_config = dict(type='single', value=val)
            cur_val = self.sim.format_parameter_value(param_config, precision)
            if default_val is None:
                default_val = cur_val
            cur_dict[name] = self.sim.format_parameter_value(param_config, precision)
        self.parameters[name] = default_val

    def set_sweep_parameter(self, name, precision=6, **kwargs):
        # type: (str, int, **Any) -> None
        """Set to sweep the given parameter.

        To set the sweep values directly:

        tb.set_sweep_parameter('var', values=[1.0, 5.0, 10.0])

        To set a linear sweep with start/stop/step (inclusive start and stop):

        tb.set_sweep_parameter('var', start=1.0, stop=9.0, step=4.0)

        To set a logarithmic sweep with points per decade (inclusive start and stop):

        tb.set_sweep_parameter('var', start=1.0, stop=10.0, num_decade=3)

        Parameters
        ----------
        name : str
            parameter name.
        precision : int
            the parameter value will be rounded to this precision.
        **kwargs : Any
            the sweep parameters.  Refer to the above for example calls.
        """
        if 'values' in kwargs:
            param_config = dict(type='list', values=kwargs['values'])
        elif 'start' in kwargs and 'stop' in kwargs:
            start = kwargs['start']
            stop = kwargs['stop']
            if 'step' in kwargs:
                step = kwargs['step']
                param_config = dict(type='linstep', start=start, stop=stop, step=step)
            elif 'num_decade' in kwargs:
                num = kwargs['num_decade']
                param_config = dict(type='decade', start=start, stop=stop, num=num)
            else:
                raise Exception('Unsupported sweep arguments: %s' % kwargs)
        else:
            raise Exception('Unsupported sweep arguments: %s' % kwargs)

        self.parameters[name] = self.sim.format_parameter_value(param_config, precision)

    def set_simulation_environments(self, env_list):
        # type: (Sequence[str]) -> None
        """Enable the given list of simulation environments.

        If more than one simulation environment is specified, then a sweep
        will be performed.

        Parameters
        ----------
        env_list : Sequence[str]
        """
        self.sim_envs = env_list

    def set_simulation_view(self, lib_name, cell_name, sim_view):
        # type: (str, str, str) -> None
        """Set the simulation view of the given design.

        For simulation, each design may have multiple views, such as schematic,
        veriloga, extracted, etc.  This method lets you choose which view to
        use for netlisting.  the given design can be the top level design or
        an intermediate instance.

        Parameters
        ----------
        lib_name : str
            design library name.
        cell_name : str
            design cell name.
        sim_view : str
            the view to simulate with.
        """
        key = '%s__%s' % (lib_name, cell_name)
        self.config_rules[key] = sim_view

    def update_testbench(self):
        # type: () -> None
        """Commit the testbench changes to the CAD database.
        """
        config_list = []
        for key, view in self.config_rules.items():
            lib, cell = key.split('__')
            config_list.append([lib, cell, view])

        env_params = []
        for env in self.sim_envs:
            if env in self.env_parameters:
                val_table = self.env_parameters[env]
                env_params.append(list(val_table.items()))
        self.db.update_testbench(self.lib, self.cell, self.parameters, self.sim_envs, config_list,
                                 env_params)

    def run_simulation(self, precision=6, sim_tag=None):
        # type: (int, Optional[str]) -> Optional[str]
        """Run simulation.

        Parameters
        ----------
        precision : int
            the floating point number precision.
        sim_tag : Optional[str]
            optional description for this simulation run.

        Returns
        -------
        value : Optional[str]
            the save directory path.  If simulation is cancelled, return None.
        """
        coro = self.async_run_simulation(precision=precision, sim_tag=sim_tag)
        batch_async_task([coro])
        return self.save_dir

    def load_sim_results(self, hist_name, precision=6):
        # type: (str, int) -> Optional[str]
        """Load previous simulation data.

        Parameters
        ----------
        hist_name : str
            the simulation history name.
        precision : int
            the floating point number precision.

        Returns
        -------
        value : Optional[str]
            the save directory path.  If result loading is cancelled, return None.
        """
        coro = self.async_load_results(hist_name, precision=precision)
        batch_async_task([coro])
        return self.save_dir

    async def async_run_simulation(self,
                                   precision: int = 6,
                                   sim_tag: Optional[str] = None) -> str:
        """A coroutine that runs the simulation.

        Parameters
        ----------
        precision : int
            the floating point number precision.
        sim_tag : Optional[str]
            optional description for this simulation run.

        Returns
        -------
        value : str
            the save directory path.
        """
        self.save_dir = None
        self.save_dir = await self.sim.async_run_simulation(self.lib, self.cell, self.outputs,
                                                            precision=precision, sim_tag=sim_tag)
        return self.save_dir

    async def async_load_results(self, hist_name: str, precision: int = 6) -> str:
        """A coroutine that loads previous simulation data.

        Parameters
        ----------
        hist_name : str
            the simulation history name.
        precision : int
            the floating point number precision.

        Returns
        -------
        value : str
            the save directory path.
        """
        self.save_dir = None
        self.save_dir = await self.sim.async_load_results(self.lib, self.cell, hist_name,
                                                          self.outputs, precision=precision)
        return self.save_dir


def create_tech_info(bag_config_path=None):
    # type: (Optional[str]) -> TechInfo
    """Create TechInfo object."""
    if bag_config_path is None:
        if 'BAG_CONFIG_PATH' not in os.environ:
            raise Exception('BAG_CONFIG_PATH not defined.')
        bag_config_path = os.environ['BAG_CONFIG_PATH']

    bag_config = read_yaml_env(bag_config_path)
    tech_params = read_yaml_env(bag_config['tech_config_path'])
    if 'class' in tech_params:
        tech_cls = _import_class_from_str(tech_params['class'])
        tech_info = tech_cls(tech_params)
    else:
        # just make a default tech_info object as place holder.
        print('*WARNING*: No TechInfo class defined.  Using a dummy version.')
        tech_info = DummyTechInfo(tech_params)

    return tech_info


class BagProject(object):
    """The main bag controller class.

    This class mainly stores all the user configurations, and issue
    high level bag commands.

    Parameters
    ----------
    bag_config_path : Optional[str]
        the bag configuration file path.  If None, will attempt to read from
        environment variable BAG_CONFIG_PATH.
    port : Optional[int]
        the BAG server process port number.  If not given, will read from port file.

    Attributes
    ----------
    bag_config : Dict[str, Any]
        the BAG configuration parameters dictionary.
    tech_info : bag.layout.core.TechInfo
        the BAG process technology class.
    """

    def __init__(self, bag_config_path=None, port=None):
        # type: (Optional[str], Optional[int]) -> None
        if bag_config_path is None:
            if 'BAG_CONFIG_PATH' not in os.environ:
                raise Exception('BAG_CONFIG_PATH not defined.')
            bag_config_path = os.environ['BAG_CONFIG_PATH']

        self.bag_config = read_yaml_env(bag_config_path)
        bag_tmp_dir = os.environ.get('BAG_TEMP_DIR', None)

        # get port files
        if port is None:
            socket_config = self.bag_config['socket']
            if 'port_file' in socket_config:
                port, msg = _get_port_number(socket_config['port_file'])
                if msg:
                    print('*WARNING* %s' % msg)

        # create ZMQDealer object
        dealer_kwargs = {}
        dealer_kwargs.update(self.bag_config['socket'])
        del dealer_kwargs['port_file']

        # create TechInfo instance
        self.tech_info = create_tech_info(bag_config_path=bag_config_path)

        # create design module database.
        try:
            lib_defs_file = _get_config_file_abspath(self.bag_config['lib_defs'])
        except ValueError:
            lib_defs_file = ''
        sch_exc_libs = self.bag_config['database']['schematic']['exclude_libraries']
        self.dsn_db = ModuleDB(lib_defs_file, self.tech_info, sch_exc_libs, prj=self)

        if port is not None:
            # make DbAccess instance.
            dealer = ZMQDealer(port, **dealer_kwargs)
            db_cls = _import_class_from_str(self.bag_config['database']['class'])
            self.impl_db = db_cls(dealer, bag_tmp_dir, self.bag_config['database'])
            self._default_lib_path = self.impl_db.default_lib_path
        else:
            self.impl_db = None  # type: Optional[DbAccess]
            self._default_lib_path = DbAccess.get_default_lib_path(self.bag_config['database'])

        # make SimAccess instance.
        sim_cls = _import_class_from_str(self.bag_config['simulation']['class'])
        self.sim = sim_cls(bag_tmp_dir, self.bag_config['simulation'])  # type: SimAccess

    @property
    def default_lib_path(self):
        # type: () -> str
        return self._default_lib_path

    def close_bag_server(self):
        # type: () -> None
        """Close the BAG database server."""
        if self.impl_db is not None:
            self.impl_db.close()
            self.impl_db = None

    def close_sim_server(self):
        # type: () -> None
        """Close the BAG simulation server."""
        if self.sim is not None:
            self.sim.close()
            self.sim = None

    def import_design_library(self, lib_name):
        # type: (str) -> None
        """Import all design templates in the given library from CAD database.

        Parameters
        ----------
        lib_name : str
            name of the library.
        """
        if self.impl_db is None:
            raise Exception('BAG Server is not set up.')

        new_lib_path = self.bag_config['new_lib_path']
        self.impl_db.import_design_library(lib_name, self.dsn_db, new_lib_path)

    def import_sch_cellview(self, lib_name: str, cell_name: str) -> None:
        """Import the given schematic and symbol template into Python.

        This import process is done recursively.

        Parameters
        ----------
        lib_name : str
            library name.
        cell_name : str
            cell name.
        """
        if self.impl_db is None:
            raise Exception('BAG Server is not set up.')

        new_lib_path = self.bag_config['new_lib_path']
        self.impl_db.import_sch_cellview(lib_name, cell_name, self.dsn_db, new_lib_path)

    def get_cells_in_library(self, lib_name):
        # type: (str) -> Sequence[str]
        """Get a list of cells in the given library.

        Returns an empty list if the given library does not exist.

        Parameters
        ----------
        lib_name : str
            the library name.

        Returns
        -------
        cell_list : Sequence[str]
            a list of cells in the library
        """
        if self.impl_db is None:
            raise Exception('BAG Server is not set up.')

        return self.impl_db.get_cells_in_library(lib_name)

    def make_template_db(self, impl_lib, grid_specs, use_cybagoa=True, gds_lay_file='',
                         cache_dir=''):
        # type: (str, Dict[str, Any], bool, str, str) -> TemplateDB
        """Create and return a new TemplateDB instance.

        Parameters
        ----------
        impl_lib : str
            the library name to put generated layouts in.
        grid_specs : Dict[str, Any]
            the routing grid specification dictionary.
        use_cybagoa : bool
            True to enable cybagoa acceleration if available.
        gds_lay_file : str
            the GDS layout information file.
        cache_dir : str
            the cache directory name.
        """
        layers = grid_specs['layers']
        widths = grid_specs['widths']
        spaces = grid_specs['spaces']
        bot_dir = grid_specs['bot_dir']
        width_override = grid_specs.get('width_override', None)

        routing_grid = RoutingGrid(self.tech_info, layers, spaces, widths, bot_dir,
                                   width_override=width_override)
        tdb = TemplateDB('template_libs.def', routing_grid, impl_lib, use_cybagoa=use_cybagoa,
                         gds_lay_file=gds_lay_file, cache_dir=cache_dir, prj=self)

        return tdb

    def generate_cell(self,  # type: BagProject
                      specs,  # type: Dict[str, Any]
                      temp_cls=None,  # type: Optional[Type[TemplateType]]
                      gen_lay=True,  # type: bool
                      gen_sch=False,  # type: bool
                      run_lvs=False,  # type: bool
                      run_rcx=False,  # type: bool
                      use_cybagoa=True,  # type: bool
                      debug=False,  # type: bool
                      profile_fname='',  # type: str
                      use_cache=False,  # type: bool
                      save_cache=False,  # type: bool
                      **kwargs,
                      ):
        # type: (...) -> Optional[Union[pstats.Stats, Dict[str, Any]]]
        """Generate layout/schematic of a given cell from specification file.

        Parameters
        ----------
        specs : Dict[str, Any]
            the specification dictionary.
        temp_cls : Optional[Type[TemplateType]]
            the TemplateBase subclass to instantiate
            if not provided, it will be imported from lay_class entry in specs dictionary.
        gen_lay : bool
            True to generate layout.
        gen_sch : bool
            True to generate schematics.
        run_lvs : bool
            True to run LVS.
        run_rcx : bool
            True to run RCX.
        use_cybagoa : bool
            True to enable cybagoa acceleration if available.
        debug : bool
            True to print debug messages.
        profile_fname : str
            If not empty, profile layout generation, and save statistics to this file.
        use_cache : bool
            True to use cached layouts.
        save_cache : bool
            True to save instances in this template to cache.
        **kwargs :
            Additional optional arguments.

        Returns
        -------
        result: Optional[Union[pstats.Stats, Dict[str, Any]]]
            If profiling is enabled, result will be the statistics object.
            If the last thing done is layout or schematic, result will contain sch_params
            If the last thing done is lvs, in case of failure result will
            contain lvs log file in a dictionary, otherwise None
            If the last thing done is rcx, in case of failure result will
            contain rcx log file in a dictionary, otherwise None
        """
        prefix = kwargs.get('prefix', '')
        suffix = kwargs.get('suffix', '')

        grid_specs = specs['routing_grid']
        impl_lib = specs['impl_lib']
        impl_cell = specs['impl_cell']
        lay_str = specs.get('lay_class', '')
        sch_lib = specs.get('sch_lib', '')
        sch_cell = specs.get('sch_cell', '')
        params = specs['params']
        gds_lay_file = specs.get('gds_lay_file', '')
        cache_dir = specs.get('cache_dir', '')

        if temp_cls is None and lay_str:
            temp_cls = _import_class_from_str(lay_str)

        has_lay = temp_cls is not None
        if gen_lay and not has_lay:
            raise ValueError('layout_class is not specified')

        if use_cache:
            db_cache_dir = specs.get('cache_dir', '')
        else:
            db_cache_dir = ''

        result_pstat = None
        if has_lay:
            temp_db = self.make_template_db(impl_lib, grid_specs, use_cybagoa=use_cybagoa,
                                            gds_lay_file=gds_lay_file, cache_dir=db_cache_dir)

            name_list = [impl_cell]
            print('computing layout...')
            if profile_fname:
                profiler = cProfile.Profile()
                profiler.runcall(temp_db.new_template, params=params, temp_cls=temp_cls,
                                 debug=False)
                profiler.dump_stats(profile_fname)
                result_pstat = pstats.Stats(profile_fname).strip_dirs()

            temp = temp_db.new_template(params=params, temp_cls=temp_cls, debug=debug)
            print('computation done.')
            temp_list = [temp]

            if save_cache and cache_dir:
                master_list = [inst.master for inst in temp.instance_iter()]
                print('saving layouts to cache...')
                temp_db.save_to_cache(master_list, cache_dir, debug=debug)
                print('saving done.')

            if gen_lay:
                print('creating layout...')
                temp_db.batch_layout(self, temp_list, name_list, debug=debug)
                print('layout done.')

            sch_params = temp.sch_params
        else:
            sch_params = params

        if gen_sch:
            dsn = self.create_design_module(lib_name=sch_lib, cell_name=sch_cell)
            print('computing schematic...')
            dsn.design(**sch_params)
            print('creating schematic...')
            dsn.implement_design(impl_lib, top_cell_name=impl_cell, prefix=prefix,
                                 suffix=suffix)
            print('schematic done.')

        result = sch_params
        lvs_passed = False
        if run_lvs:
            print('running lvs...')
            lvs_passed, lvs_log = self.run_lvs(impl_lib, impl_cell, gds_lay_file=gds_lay_file)
            if lvs_passed:
                print('LVS passed!')
                result = dict(log='')
            else:
                raise ValueError(f'LVS failed, lvs_log: {lvs_log}')

        if run_rcx and ((run_lvs and lvs_passed) or not run_lvs):
            print('running rcx...')
            rcx_passed, rcx_log = self.run_rcx(impl_lib, impl_cell)
            if rcx_passed:
                print('RCX passed!')
                result = dict(log='')
            else:
                raise ValueError(f'RCX failed, rcx_log: {rcx_log}')

        if result_pstat:
            return result_pstat
        return result

    def replace_dut_in_wrapper(self, params: Dict[str, Any], dut_lib: str,
                               dut_cell: str) -> None:
        # helper function that replaces dut_lib and dut_cell in the wrapper recursively base on
        # dut_params
        dut_params = params.get('dut_params', None)
        if dut_params is None:
            params['dut_lib'] = dut_lib
            params['dut_cell'] = dut_cell
            return
        return self.replace_dut_in_wrapper(dut_params, dut_lib, dut_cell)

    def simulate_cell(self,
                      specs: Dict[str, Any],
                      gen_cell: bool = True,
                      gen_wrapper: bool = True,
                      gen_tb: bool = True,
                      load_results: bool = False,
                      extract: bool = False,
                      run_sim: bool = True) -> Optional[Dict[str, Any]]:
        """
        Runs a minimum executable parts of the Testbench Manager flow selectively according to
        a spec dictionary.

        For example you can set the flags to generate a new cell, but since wrapper and test bench
        exist, maybe you want to skip those, and run the simulation in the end. Maybe you
        already created the cell all the way up to test bench level, and now you only need to
        run simulation.

        This function only works with Testbench Managers written in format of
        simulation.core_v2.TestbenchManager

        Parameters
        ----------
        specs:
            Dictionary of specifications
            Some non-obvious conventions:
            - if contains tbm_specs keyword, simulation is ran through testbench manager v2,
            otherwise there should be a sim_params entry that specifies the simulation.
            - Wrapper is assumed to be in the specs dictionary, if it is generated outside of
            this function, gen_wrapper should be False.
        gen_cell:
            True to call generate_cell on specs
        gen_wrapper:
            True to generate Wrapper. Currently only one top-level wrapper is supported.
        gen_tb:
            True to generate test bench. If test bench is created, this flag can be set to False.
        load_results:
            True to skip simulation and load the results.
        extract:
            False to skip layout generation and only simulate schematic
        run_sim:
            True to run simulation. If the purpose of calling this function is just to generate
            some part of simulation flow to debug, this flag can be set to False.
        Returns
        -------
        results: Optional[Dict[str, Any]]
            if run_sim/load_results = True, contains simulations results, otherwise it's None.
        """

        impl_lib = specs['impl_lib']
        impl_cell = specs['impl_cell']
        root_dir = Path(specs['root_dir'])

        if gen_cell and not load_results:
            print('generating cell ...')
            self.generate_cell(specs,
                               gen_lay=extract,
                               gen_sch=True,
                               run_lvs=extract,
                               run_rcx=extract,
                               use_cybagoa=True)
            print('cell generated.')

        # if testbench manager v2 found use that instead of interpreting simulation directly
        tbm_specs = specs.get('tbm_specs', None)
        if tbm_specs:
            tbm_cls_str = tbm_specs['tbm_cls']
            tbm_cls = _import_class_from_str(tbm_cls_str)
            tbm: TestbenchManager = tbm_cls(root_dir)
            sim_view_list = tbm_specs.get('sim_view_list', [])
            if not sim_view_list:
                view_name = 'netlist' if extract else 'schematic'
                sim_view_list.append((impl_cell, view_name))
            sim_envs = tbm_specs['sim_envs']

            if load_results:
                return tbm.load_results(impl_cell, tbm_specs)

            results = tbm.simulate(bprj=self,
                                   impl_lib=impl_lib,
                                   impl_cell=impl_cell,
                                   sim_view_list=sim_view_list,
                                   env_list=sim_envs,
                                   tb_dict=tbm_specs,
                                   wrapper_dict=None,
                                   gen_tb=gen_tb,
                                   gen_wrapper=gen_wrapper,
                                   run_sim=run_sim)
            return results

        sim_params = specs.get('sim_params', None)
        wrapper = sim_params.get('wrapper', None)

        has_wrapper = wrapper is not None
        if gen_wrapper and not has_wrapper:
            raise ValueError('must provide a wrapper in sim_params')

        wrapper_lib = wrapper_cell = wrapped_cell = wrapper_params = None
        if has_wrapper:
            wrapper_lib = wrapper['wrapper_lib']
            wrapper_cell = wrapper['wrapper_cell']
            wrapper_params = wrapper.get('params', {})
            wrapper_suffix = wrapper.get('wrapper_suffix', '')
            if not wrapper_suffix:
                wrapper_suffix = f'{wrapper_cell}'
            wrapped_cell = f'{impl_cell}_{wrapper_suffix}'

        if gen_wrapper and not gen_tb:
            raise ValueError('generated a new wrapper, therefore gen_tb should also be true')

        tb_lib = sim_params['tb_lib']
        tb_cell = sim_params['tb_cell']
        tb_params = sim_params.get('tb_params', {})
        tb_suffix = sim_params.get('tb_suffix', '')
        if not tb_suffix:
            tb_suffix = f'{tb_cell}'
        tb_name = f'{impl_cell}_{tb_suffix}'

        tb_fname = root_dir / Path(tb_name, f'{tb_name}.hdf5')

        if load_results:
            print("loading results ...")
            if tb_fname.exists():
                return sim_data.load_sim_file(tb_fname)
            raise ValueError(f'simulation results does not exist in {str(tb_fname)}')

        if gen_wrapper and has_wrapper:
            print('generating wrapper ...')
            master = self.create_design_module(lib_name=wrapper_lib, cell_name=wrapper_cell)
            self.replace_dut_in_wrapper(wrapper_params, impl_lib, impl_cell)
            master.design(**wrapper_params)
            master.implement_design(impl_lib, wrapped_cell)
            print('wrapper generated.')

        if gen_tb:
            print('generating testbench ...')
            tb_master = self.create_design_module(tb_lib, tb_cell)
            dut_cell = wrapped_cell if has_wrapper else impl_cell
            tb_master.design(dut_lib=impl_lib, dut_cell=dut_cell, **tb_params)
            tb_master.implement_design(impl_lib, tb_name)
            print('testbench generated.')

        if run_sim:
            print('setting up ADEXL ...')
            sim_view_list = sim_params.get('sim_view_list', [])
            if not sim_view_list:
                view_name = 'netlist' if extract else 'schematic'
                sim_view_list.append((impl_cell, view_name))

            sim_envs = sim_params['sim_envs']
            sim_swp_params = sim_params.get('sim_swp_params', {})
            sim_vars = sim_params.get('sim_vars', {})
            sim_outputs = sim_params.get('sim_outputs', {})

            tb = self.configure_testbench(impl_lib, tb_name)

            # set simulation variables
            for key, val in sim_vars.items():
                tb.set_parameter(key, val)

            # set sweep parameters
            for key, val in sim_swp_params.items():
                tb.set_sweep_parameter(key, **val)

            # set the simulation outputs
            for key, val in sim_outputs.items():
                tb.add_output(key, val)

            # change the view_name (netlist or schematic)
            for cell, view in sim_view_list:
                tb.set_simulation_view(impl_lib, cell, view)

            tb.set_simulation_environments(sim_envs)
            tb.update_testbench()
            print('setup completed.')
            print('running simulation ...')
            tb.run_simulation()
            print('simulation done.')
            print('loading results ...')
            results = sim_data.load_sim_results(tb.save_dir)
            if not results.get('sweep_params', {}):
                raise ValueError(f'results are empty, either you forgot to specify outputs, or '
                                 f'simulation failed. check sim_log: {tb.save_dir}/ocn_output.log')
            print('results loaded.')
            print('saving results into hdf5')
            sim_data.save_sim_results(results, tb_fname)
            print('results saved.')
            return results

    def measure_cell(self,
                     specs: Dict[str, Any],
                     gen_cell: bool = True,
                     gen_wrapper: bool = True,
                     gen_tb: bool = True,
                     load_results: bool = False,
                     extract: bool = False,
                     run_sims: bool = True) -> Optional[Dict[str, Any]]:
        """
        Runs a minimum executable parts of the Measurement Manager flow selectively according to
        a spec dictionary.

        For example you can set the flags to generate a new cell, but since wrapper and test bench
        exist, maybe you want to skip those, and run the measurement in the end. Maybe you
        already created the cell all the way up to test bench level, and now you only need to
        run simulation.

        This function only works with Measurement Managers written in format of
        simulation.core_v2.MeasurementManager

        Parameters
        ----------
        specs:
            Dictionary of specifications
            Some non-obvious conventions:
            - if contains tbm_specs keyword, simulation is ran through testbench manager v2,
            otherwise there should be a sim_params entry that specifies the simulation.
            - Wrapper is assumed to be in the specs dictionary, if it is generated outside of
            this function, gen_wrapper should be False.
        gen_cell:
            True to call generate_cell on specs
        gen_wrapper:
            True to generate Wrapper. Currently only one top-level wrapper is supported.
        gen_tb:
            True to generate test bench. If test bench is created, this flag can be set to False.
        load_results:
            True to skip simulation and load the results.
        extract:
            False to skip layout generation and only simulate schematic
        run_sims:
            True to run simulations. If the purpose of calling this function is just to generate
            some part of simulation flow to debug, this flag can be set to False.
        Returns
        -------
        results: Optional[Dict[str, Any]]
            if run_sim/load_results = True, contains measurement results, otherwise it's None.
        """

        impl_lib = specs['impl_lib']
        impl_cell = specs['impl_cell']
        root_dir = Path(specs['root_dir'])

        if gen_cell and not load_results:
            print('generating cell ...')
            self.generate_cell(specs,
                               gen_lay=extract,
                               gen_sch=True,
                               run_lvs=extract,
                               run_rcx=extract,
                               use_cybagoa=True)
            print('cell generated.')

        mm_specs = specs['mm_specs']
        mm_cls_str = mm_specs['mm_cls']
        mm_cls = _import_class_from_str(mm_cls_str)
        mm: MeasurementManager = mm_cls(root_dir, mm_specs)
        return mm.measure(self, impl_lib, impl_cell, load_results=load_results,
                          gen_wrapper=gen_wrapper, gen_tb=gen_tb, run_sims=run_sims,
                          extract=extract)

    def create_library(self, lib_name, lib_path=''):
        # type: (str, str) -> None
        """Create a new library if one does not exist yet.

        Parameters
        ----------
        lib_name : str
            the library name.
        lib_path : str
            directory to create the library in.  If Empty, use default location.
        """
        if self.impl_db is None:
            raise Exception('BAG Server is not set up.')

        return self.impl_db.create_library(lib_name, lib_path=lib_path)

    # noinspection PyUnusedLocal
    def create_design_module(self, lib_name, cell_name, **kwargs):
        # type: (str, str, **Any) -> SchInstance
        """Create a new top level design module for the given schematic template

        Parameters
        ----------
        lib_name : str
            the library name.
        cell_name : str
            the cell name.
        **kwargs : Any
            optional parameters.

        Returns
        -------
        dsn : SchInstance
            a configurable schematic instance of the given schematic generator.
        """
        return SchInstance(self.dsn_db, lib_name, cell_name, 'XTOP', static=False)

    def new_schematic_instance(self, lib_name='', cell_name='', params=None, sch_cls=None,
                               debug=False, **kwargs):
        # type: (str, str, Dict[str, Any], Type[ModuleType], bool, **Any) -> SchInstance
        """Create a new schematic instance

        This method is the schematic equivalent of TemplateDB's new_template() method.
        By default, we assume the design() function is used to set the schematic parameters.
        If you use another function (such as design_specs()), then you should specify
        an optional parameter design_fun equal to the name of that function.

        Parameters
        ----------
        lib_name : str
            schematic library name.
        cell_name : str
            schematic name
        params : Dict[str, Any]
            the parameter dictionary.
        sch_cls : Type[TemplateType]
            the schematic generator class to instantiate.
        debug : bool
            True to print debug messages.
        **kwargs : Any
            optional parameters.

        Returns
        -------
        dsn : SchInstance
            a schematic instance of the given schematic generator.
        """
        design_fun = kwargs.get('design_fun', 'design')
        master = self.dsn_db.new_master(lib_name, cell_name, gen_cls=sch_cls, params=params,
                                        debug=debug, design_args=None, design_fun=design_fun)

        return SchInstance(self.dsn_db, lib_name, cell_name, 'XTOP', static=False,
                           master=master)

    def clear_schematic_database(self):
        # type: () -> None
        """Reset schematic database."""
        self.dsn_db.clear()

    def instantiate_schematic(self, lib_name, content_list, lib_path=''):
        # type: (str, Sequence[Any], str) -> None
        """Create the given schematic contents in CAD database.

        NOTE: this is BAG's internal method.  TO create schematics, call batch_schematic() instead.

        Parameters
        ----------
        lib_name : str
            name of the new library to put the schematic instances.
        content_list : Sequence[Any]
            list of schematics to create.
        lib_path : str
            the path to create the library in.  If empty, use default location.
        """
        if self.impl_db is None:
            raise Exception('BAG Server is not set up.')

        self.impl_db.instantiate_schematic(lib_name, content_list, lib_path=lib_path)

    def batch_schematic(self,  # type: BagProject
                        lib_name,  # type: str
                        sch_inst_list,  # type: Sequence[SchInstance]
                        name_list=None,  # type: Optional[Sequence[Optional[str]]]
                        prefix='',  # type: str
                        suffix='',  # type: str
                        debug=False,  # type: bool
                        rename_dict=None,  # type: Optional[Dict[str, str]]
                        ):
        # type: (...) -> None
        """create all the given schematics in CAD database.

        Parameters
        ----------
        lib_name : str
            name of the new library to put the schematic instances.
        sch_inst_list : Sequence[SchInstance]
            list of SchInstance objects.
        name_list : Optional[Sequence[Optional[str]]]
            list of master cell names.  If not given, default names will be used.
        prefix : str
            prefix to add to cell names.
        suffix : str
            suffix to add to cell names.
        debug : bool
            True to print debugging messages
        rename_dict : Optional[Dict[str, str]]
            optional master cell renaming dictionary.
        """
        master_list = [inst.master for inst in sch_inst_list]

        self.dsn_db.cell_prefix = prefix
        self.dsn_db.cell_suffix = suffix
        self.dsn_db.instantiate_masters(master_list, name_list=name_list, lib_name=lib_name,
                                        debug=debug, rename_dict=rename_dict)

    def configure_testbench(self, tb_lib, tb_cell):
        # type: (str, str) -> Testbench
        """Update testbench state for the given testbench.

        This method fill in process-specific information for the given testbench, then returns
        a testbench object which you can use to control simulation.

        Parameters
        ----------
        tb_lib : str
            testbench library name.
        tb_cell : str
            testbench cell name.

        Returns
        -------
        tb : :class:`bag.core.Testbench`
            the :class:`~bag.core.Testbench` instance.
        """
        if self.impl_db is None:
            raise Exception('BAG Server is not set up.')
        if self.sim is None:
            raise Exception('SimAccess is not set up.')

        c, clist, params, outputs = self.impl_db.configure_testbench(tb_lib, tb_cell)
        return Testbench(self.sim, self.impl_db, tb_lib, tb_cell, params, clist, [c], outputs)

    def load_testbench(self, tb_lib, tb_cell):
        # type: (str, str) -> Testbench
        """Loads a testbench from the database.

        Parameters
        ----------
        tb_lib : str
            testbench library name.
        tb_cell : str
            testbench cell name.

        Returns
        -------
        tb : :class:`bag.core.Testbench`
            the :class:`~bag.core.Testbench` instance.
        """
        if self.impl_db is None:
            raise Exception('BAG Server is not set up.')
        if self.sim is None:
            raise Exception('SimAccess is not set up.')

        cur_envs, all_envs, params, outputs = self.impl_db.get_testbench_info(tb_lib, tb_cell)
        return Testbench(self.sim, self.impl_db, tb_lib, tb_cell, params, all_envs,
                         cur_envs, outputs)

    def instantiate_layout_pcell(self, lib_name, cell_name, inst_lib, inst_cell, params,
                                 pin_mapping=None, view_name='layout'):
        # type: (str, str, str, str, Dict[str, Any], Optional[Dict[str, str]], str) -> None
        """Create a layout cell with a single pcell instance.

        Parameters
        ----------
        lib_name : str
            layout library name.
        cell_name : str
            layout cell name.
        inst_lib : str
            pcell library name.
        inst_cell : str
            pcell cell name.
        params : Dict[str, Any]
            the parameter dictionary.
        pin_mapping: Optional[Dict[str, str]]
            the pin renaming dictionary.
        view_name : str
            layout view name, default is "layout".
        """
        if self.impl_db is None:
            raise Exception('BAG Server is not set up.')

        pin_mapping = pin_mapping or {}
        self.impl_db.instantiate_layout_pcell(lib_name, cell_name, view_name,
                                              inst_lib, inst_cell, params, pin_mapping)

    def instantiate_layout(self, lib_name, view_name, via_tech, layout_list):
        # type: (str, str, str, Sequence[Any]) -> None
        """Create a batch of layouts.

        Parameters
        ----------
        lib_name : str
            layout library name.
        view_name : str
            layout view name.
        via_tech : str
            via technology name.
        layout_list : Sequence[Any]
            a list of layouts to create
        """
        if self.impl_db is None:
            raise Exception('BAG Server is not set up.')

        self.impl_db.instantiate_layout(lib_name, view_name, via_tech, layout_list)

    def release_write_locks(self, lib_name, cell_view_list):
        # type: (str, Sequence[Tuple[str, str]]) -> None
        """Release write locks from all the given cells.

        Parameters
        ----------
        lib_name : str
            the library name.
        cell_view_list : Sequence[Tuple[str, str]]
            list of cell/view name tuples.
        """
        if self.impl_db is None:
            raise Exception('BAG Server is not set up.')

        self.impl_db.release_write_locks(lib_name, cell_view_list)

    def run_lvs(self,  # type: BagProject
                lib_name,  # type: str
                cell_name,  # type: str
                **kwargs
                ):
        # type: (...) -> Tuple[bool, str]
        """Run LVS on the given cell.

        Parameters
        ----------
        lib_name : str
            library name.
        cell_name : str
            cell_name
        **kwargs :
            optional keyword arguments.  See DbAccess class for details.

        Returns
        -------
        value : bool
            True if LVS succeeds
        log_fname : str
            name of the LVS log file.
        """
        if self.impl_db is None:
            raise Exception('BAG Server is not set up.')

        coro = self.impl_db.async_run_lvs(lib_name, cell_name, **kwargs)
        results = batch_async_task([coro])
        if results is None or isinstance(results[0], Exception):
            return False, ''
        return results[0]

    def run_rcx(self,  # type: BagProject
                lib_name,  # type: str
                cell_name,  # type: str
                **kwargs
                ):
        # type: (...) -> Tuple[Union[bool, Optional[str]], str]
        """Run RCX on the given cell.

        The behavior and the first return value of this method depends on the
        input arguments.  The second return argument will always be the RCX
        log file name.

        If create_schematic is True, this method will run RCX, then if it succeeds,
        create a schematic of the extracted netlist in the database.  It then returns
        a boolean value which will be True if RCX succeeds.

        If create_schematic is False, this method will run RCX, then return a string
        which is the extracted netlist filename. If RCX failed, None will be returned
        instead.

        Parameters
        ----------
        lib_name : str
            library name.
        cell_name : str
            cell_name
            override RCX parameter values.
        **kwargs :
            optional keyword arguments.  See DbAccess class for details.

        Returns
        -------
        value : Union[bool, str]
            The return value, as described.
        log_fname : str
            name of the RCX log file.
        """
        if self.impl_db is None:
            raise Exception('BAG Server is not set up.')

        create_schematic = kwargs.get('create_schematic', True)

        coro = self.impl_db.async_run_rcx(lib_name, cell_name, **kwargs)
        results = batch_async_task([coro])
        if results is None or isinstance(results[0], Exception):
            if create_schematic:
                return False, ''
            else:
                return None, ''
        return results[0]

    def export_layout(self, lib_name, cell_name, out_file, **kwargs):
        # type: (str, str, str, **Any) -> str
        """export layout.

        Parameters
        ----------
        lib_name : str
            library name.
        cell_name : str
            cell name.
        out_file : str
            output file name.
        **kwargs : Any
            optional keyword arguments.  See Checker class for details.

        Returns
        -------
        log_fname : str
            log file name.  Empty if task cancelled.
        """
        if self.impl_db is None:
            raise Exception('BAG Server is not set up.')

        coro = self.impl_db.async_export_layout(lib_name, cell_name, out_file, **kwargs)
        results = batch_async_task([coro])
        if results is None or isinstance(results[0], Exception):
            return ''
        return results[0]

    def batch_export_layout(self, info_list):
        # type: (Sequence[Tuple[Any, ...]]) -> Optional[Sequence[str]]
        """Export layout of all given cells

        Parameters
        ----------
        info_list:
            list of cell information.  Each element is a tuple of:

            lib_name : str
                library name.
            cell_name : str
                cell name.
            out_file : str
                layout output file name.
            view_name : str
                layout view name.  Optional.
            params : Optional[Dict[str, Any]]
                optional export parameter values.

        Returns
        -------
        results : Optional[Sequence[str]]
            If task is cancelled, return None.  Otherwise, this is a
            list of log file names.
        """
        if self.impl_db is None:
            raise Exception('BAG Server is not set up.')

        coro_list = [self.impl_db.async_export_layout(*info) for info in info_list]
        temp_results = batch_async_task(coro_list)
        if temp_results is None:
            return None
        return ['' if isinstance(val, Exception) else val for val in temp_results]

    async def async_run_lvs(self, lib_name: str, cell_name: str, **kwargs: Any) -> Tuple[bool, str]:
        """A coroutine for running LVS.

        Parameters
        ----------
        lib_name : str
            library name.
        cell_name : str
            cell_name
        **kwargs : Any
            optional keyword arguments.  See Checker class for details.
            LVS parameters should be specified as lvs_params.

        Returns
        -------
        value : bool
            True if LVS succeeds
        log_fname : str
            name of the LVS log file.
        """
        if self.impl_db is None:
            raise Exception('BAG Server is not set up.')

        return await self.impl_db.async_run_lvs(lib_name, cell_name, **kwargs)

    async def async_run_rcx(self,  # type: BagProject
                            lib_name: str,
                            cell_name: str,
                            **kwargs
                            ) -> Tuple[Union[bool, Optional[str]], str]:
        """Run RCX on the given cell.

        The behavior and the first return value of this method depends on the
        input arguments.  The second return argument will always be the RCX
        log file name.

        If create_schematic is True, this method will run RCX, then if it succeeds,
        create a schematic of the extracted netlist in the database.  It then returns
        a boolean value which will be True if RCX succeeds.

        If create_schematic is False, this method will run RCX, then return a string
        which is the extracted netlist filename. If RCX failed, None will be returned
        instead.

        Parameters
        ----------
        lib_name : str
            library name.
        cell_name : str
            cell_name
            override RCX parameter values.
        **kwargs :
            optional keyword arguments.  See DbAccess class for details.

        Returns
        -------
        value : Union[bool, str]
            The return value, as described.
        log_fname : str
            name of the RCX log file.
        """
        if self.impl_db is None:
            raise Exception('BAG Server is not set up.')

        return await self.impl_db.async_run_rcx(lib_name, cell_name, **kwargs)

    def create_schematic_from_netlist(self, netlist, lib_name, cell_name,
                                      sch_view=None, **kwargs):
        # type: (str, str, str, Optional[str], **Any) -> None
        """Create a schematic from a netlist.

        This is mainly used to create extracted schematic from an extracted netlist.

        Parameters
        ----------
        netlist : str
            the netlist file name.
        lib_name : str
            library name.
        cell_name : str
            cell_name
        sch_view : Optional[str]
            schematic view name.  The default value is implemendation dependent.
        **kwargs : Any
            additional implementation-dependent arguments.
        """
        if self.impl_db is None:
            raise Exception('BAG Server is not set up.')

        return self.impl_db.create_schematic_from_netlist(netlist, lib_name, cell_name,
                                                          sch_view=sch_view, **kwargs)

    def create_verilog_view(self, verilog_file, lib_name, cell_name, **kwargs):
        # type: (str, str, str, **Any) -> None
        """Create a verilog view for mix-signal simulation.

        Parameters
        ----------
        verilog_file : str
            the verilog file name.
        lib_name : str
            library name.
        cell_name : str
            cell name.
        **kwargs : Any
            additional implementation-dependent arguments.
        """
        if self.impl_db is None:
            raise Exception('BAG Server is not set up.')

        verilog_file = os.path.abspath(verilog_file)
        if not os.path.isfile(verilog_file):
            raise ValueError('%s is not a file.' % verilog_file)

        return self.impl_db.create_verilog_view(verilog_file, lib_name, cell_name, **kwargs)


================================================
FILE: bag/data/LICENSE
================================================
BSD 3-Clause License

Copyright (c) 2018, Regents of the University of California
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

* Redistributions of source code must retain the above copyright notice, this
  list of conditions and the following disclaimer.

* Redistributions in binary form must reproduce the above copyright notice,
  this list of conditions and the following disclaimer in the documentation
  and/or other materials provided with the distribution.

* Neither the name of the copyright holder nor the names of its
  contributors may be used to endorse or promote products derived from
  this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.


================================================
FILE: bag/data/__init__.py
================================================
# -*- coding: utf-8 -*-

"""This package defines methods and classes useful for data post-processing.
"""

# compatibility import.
from ..io import load_sim_results, save_sim_results, load_sim_file
from .core import Waveform

__all__ = ['load_sim_results', 'save_sim_results', 'load_sim_file',
           'Waveform', ]


================================================
FILE: bag/data/core.py
================================================
# -*- coding: utf-8 -*-

"""This module defines core data post-processing classes.
"""

import numpy as np
import scipy.interpolate as interp
import scipy.cluster.vq as svq
import scipy.optimize as sciopt


class Waveform(object):
    """A (usually transient) waveform.

    This class provides interpolation and other convenience functions.

    Parameters
    ----------
    xvec : np.multiarray.ndarray
        the X vector.
    yvec : np.multiarray.ndarray
        the Y vector.
    xtol : float
        the X value tolerance.
    order : int
        the interpolation order.  1 for nearest, 2 for linear, 3 for spline.
    ext : int or str
        interpolation extension mode.  See documentation for InterpolatedUnivariateSpline.

    """
    def __init__(self, xvec, yvec, xtol, order=3, ext=3):
        self._xvec = xvec
        self._yvec = yvec
        self._xtol = xtol
        self._order = order
        self._ext = ext
        self._fun = interp.InterpolatedUnivariateSpline(xvec, yvec, k=order, ext=ext)

    @property
    def xvec(self):
        """the X vector"""
        return self._xvec

    @property
    def yvec(self):
        """the Y vector"""
        return self._yvec

    @property
    def order(self):
        """the interpolation order.  1 for nearest, 2 for linear, 3 for spline."""
        return self._order

    @property
    def xtol(self):
        """the X value tolerance."""
        return self._xtol

    @property
    def ext(self):
        """interpolation extension mode.  See documentation for InterpolatedUnivariateSpline."""
        return self._ext

    def __call__(self, *arg, **kwargs):
        """Evaluate the waveform at the given points."""
        return self._fun(*arg, **kwargs)

    def get_xrange(self):
        """Returns the X vector range.

        Returns
        -------
        xmin : float
            minimum X value.
        xmax : float
            maximum X value.
        """
        return self.xvec[0], self.xvec[-1]

    def shift_by(self, xshift):
        """Returns a shifted version of this waveform.

        Parameters
        ----------
        xshift : float
            the amount to shift by.

        Returns
        -------
        wvfm : bag.data.core.Waveform
            a reference to this instance, or a copy if copy is True.
        """
        return Waveform(self.xvec + xshift, self.yvec, self.xtol, order=self.order, ext=self.ext)

    def get_all_crossings(self, threshold, start=None, stop=None, edge='both'):
        """Returns all X values at which this waveform crosses the given threshold.

        Parameters
        ----------
        threshold : float
            the threshold value.
        start : float or None
            if given, search for crossings starting at this X value.
        stop : float or None
            if given, search only for crossings before this X value.
        edge : string
            crossing type.  Valid values are 'rising', 'falling', or 'both'.

        Returns
        -------
        xval_list : list[float]
            all X values at which crossing occurs.
        """
        # determine start and stop indices
        sidx = 0 if start is None else np.searchsorted(self.xvec, [start])[0]
        if stop is None:
            eidx = len(self.xvec)
        else:
            eidx = np.searchsorted(self.xvec, [stop])[0]
            if eidx < len(self.xvec) and abs(self.xvec[eidx] - stop) < self.xtol:
                eidx += 1

        # quantize waveform values, then detect edge.
        bool_vec = self.yvec[sidx:eidx] >= threshold  # type: np.ndarray
        qvec = bool_vec.astype(int)
        dvec = np.diff(qvec)

        # eliminate unwanted edge types.
        if edge == 'rising':
            dvec = np.maximum(dvec, 0)
        elif edge == 'falling':
            dvec = np.minimum(dvec, 0)

        # get crossing indices
        idx_list = dvec.nonzero()[0]

        # convert indices to X value using brentq interpolation.
        def crossing_fun(x):
            return self._fun(x) - threshold

        xval_list = []
        for idx in idx_list:
            t0, t1 = self.xvec[sidx + idx], self.xvec[sidx + idx + 1]
            try:
                tcross = sciopt.brentq(crossing_fun, t0, t1, xtol=self.xtol)
            except ValueError:
                # no solution, this happens only if we have numerical error
                # around the threshold.  In this case just pick the endpoint
                # closest to threshold.
                va = crossing_fun(t0)
                vb = crossing_fun(t1)
                tcross = t0 if abs(va) < abs(vb) else t1

            xval_list.append(tcross)

        return xval_list

    def get_crossing(self, threshold, start=None, stop=None, n=1, edge='both'):
        """Returns the X value at which this waveform crosses the given threshold.

        Parameters
        ----------
        threshold : float
            the threshold value.
        start : float or None
            if given, search for the crossing starting at this X value.'
        stop : float or None
            if given, search only for crossings before this X value.
        n : int
            returns the nth crossing.
        edge : str
            crossing type.  Valid values are 'rising', 'falling', or 'both'.

        Returns
        -------
        xval : float or None
            the X value at which the crossing occurs.  None if no crossings are detected.
        """
        xval_list = self.get_all_crossings(threshold, start=start, stop=stop, edge=edge)
        if len(xval_list) < n:
            return None
        return xval_list[n-1]

    def to_arrays(self, xmin=None, xmax=None):
        """Returns the X and Y arrays representing this waveform.

        Parameters
        ----------
        xmin : float or None
            If given, will start from this value.
        xmax : float or None
            If given, will end at this value.

        Returns
        -------
        xvec : np.multiarray.ndarray
            the X array
        yvec : np.multiarray.ndarray
            the Y array
        """
        sidx = 0 if xmin is None else np.searchsorted(self.xvec, [xmin])[0]
        eidx = len(self.xvec) if xmax is None else np.searchsorted(self.xvec, [xmax])[0]

        if eidx < len(self.xvec) and self.xvec[eidx] == xmax:
            eidx += 1

        xtemp = self.xvec[sidx:eidx]
        if xmin is not None and (len(xtemp) == 0 or xtemp[0] != xmin):
            np.insert(xtemp, 0, [xmin])
        if xmax is not None and (len(xtemp) == 0 or xtemp[-1] != xmax):
            np.append(xtemp, [xmax])
        return xtemp, self(xtemp)

    def get_eye_specs(self, tbit, tsample, thres=0.0, nlev=2):
        """Compute the eye diagram spec of this waveform.

        This algorithm uses the following steps.

        1. set t_off to 0
        2. sample the waveform at tbit interval, starting at t0 + t_off.
        3. sort the sampled values, get gap between adjacent values.
        4. record G, the length of the gap covering thres.
        5. increment t_off by tsample, go to step 2 and repeat until
           t_off >= tbit.
        6. find t_off with maximum G.  This is the eye center.
        7. at the eye center, compute eye height and eye opening using kmeans
           clustering algorithm.
        8. return result.

        Parameters
        ----------
        tbit : float
            eye period.
        tsample : float
            the resolution to sample the eye.  Used to find optimal
            time shift and maximum eye opening.
        thres : float
            the eye vertical threshold.
        nlev : int
            number of expected levels.  2 for NRZ, 4 for PAM4.

        Returns
        -------
        result : dict
            A dictionary from specification to value.
        """

        tstart, tend = self.get_xrange()
        toff_vec = np.arange(0, tbit, tsample)
        best_idx = 0
        best_gap = 0.0
        best_values = None
        mid_lev = nlev // 2
        for idx, t_off in enumerate(toff_vec):
            # noinspection PyTypeChecker
            values = self(np.arange(tstart + t_off, tend, tbit))
            values.sort()

            up_idx = np.searchsorted(values, [thres])[0]
            if up_idx == 0 or up_idx == len(values):
                continue
            cur_gap = values[up_idx] - values[up_idx - 1]
            if cur_gap > best_gap:
                best_idx = idx
                best_gap = cur_gap
                best_values = values

        if best_values is None:
            raise ValueError("waveform never cross threshold=%.4g" % thres)

        vstd = np.std(best_values)
        vtemp = best_values / vstd
        tmp_arr = np.linspace(vtemp[0], vtemp[-1], nlev)  # type: np.ndarray
        clusters = svq.kmeans(vtemp, tmp_arr)[0]
        # clusters = svq.kmeans(vtemp, 4, iter=50)[0]
        clusters *= vstd
        clusters.sort()
        vcenter = (clusters[mid_lev] + clusters[mid_lev - 1]) / 2.0

        # compute eye opening/margin
        openings = []
        tr_widths = []
        last_val = best_values[0]
        bot_val = last_val
        cur_cidx = 0
        for cur_val in best_values:
            cur_cluster = clusters[cur_cidx]
            next_cluster = clusters[cur_cidx + 1]
            if abs(cur_val - cur_cluster) > abs(cur_val - next_cluster):
                openings.append(cur_val - last_val)
                tr_widths.append(last_val - bot_val)
                cur_cidx += 1
                if cur_cidx == len(clusters) - 1:
                    tr_widths.append(best_values[-1] - cur_val)
                    break
                bot_val = cur_val
            last_val = cur_val

        return {'center': (float(toff_vec[best_idx]), vcenter),
                'levels': clusters,
                'heights': clusters[1:] - clusters[:-1],
                'openings': np.array(openings),
                'trace_widths': np.array(tr_widths)
                }

    def _add_xy(self, other):
        if not isinstance(other, Waveform):
            raise ValueError("Trying to add non-Waveform object.")
        xnew = np.concatenate((self.xvec, other.xvec))
        xnew = np.unique(np.around(xnew / self.xtol)) * self.xtol
        # noinspection PyTypeChecker
        y1 = self(xnew)
        y2 = other(xnew)
        return xnew, y1 + y2

    def __add__(self, other):
        if np.isscalar(other):
            return Waveform(np.array(self.xvec), self.yvec + other, self.xtol, order=self.order, ext=self.ext)
        elif isinstance(other, Waveform):
            new_order = max(self.order, other.order)
            xvec, yvec = self._add_xy(other)
            return Waveform(xvec, yvec, self.xtol, order=new_order, ext=self.ext)
        else:
            raise Exception('type %s not supported' % type(other))

    def __neg__(self):
        return Waveform(np.array(self.xvec), -self.yvec, self.xtol, order=self.order, ext=self.ext)

    def __mul__(self, scale):
        if not np.isscalar(scale):
            raise ValueError("Can only multiply by scalar.")
        return Waveform(np.array(self.xvec), scale * self.yvec, self.xtol, order=self.order, ext=self.ext)

    def __rmul__(self, scale):
        return self.__mul__(scale)


================================================
FILE: bag/data/dc.py
================================================
# -*- coding: utf-8 -*-

"""This module defines classes for computing DC operating point.
"""

from typing import Union, Dict

import scipy.sparse
import scipy.optimize
import numpy as np

from bag.tech.mos import MosCharDB


class DCCircuit(object):
    """A class that solves DC operating point of a circuit.

    Parameters
    ----------
    ndb : MosCharDB
        nmos characterization database.
    pdb : MosCharDB
        pmos characterization database.
    """

    def __init__(self, ndb, pdb):
        # type: (MosCharDB, MosCharDB) -> None
        self._n = 1
        self._ndb = ndb
        self._pdb = pdb
        self._transistors = {}
        self._node_id = {'gnd': 0, 'vss': 0, 'VSS': 0}
        self._node_name_lookup = {0: 'gnd'}
        self._node_voltage = {0: 0}

    def _get_node_id(self, name):
        # type: (str) -> int
        if name not in self._node_id:
            ans = self._n
            self._node_id[name] = ans
            self._node_name_lookup[ans] = name
            self._n += 1
            return ans
        else:
            return self._node_id[name]

    def set_voltage_source(self, node_name, voltage):
        # type: (str, float) -> None
        """
        Specify voltage the a node.

        Parameters
        ----------
        node_name : str
            the net name.
        voltage : float
            voltage of the given net.
        """
        node_id = self._get_node_id(node_name)
        self._node_voltage[node_id] = voltage

    def add_transistor(self, d_name, g_name, s_name, b_name, mos_type, intent, w, lch, fg=1):
        # type: (str, str, str, str, str, str, Union[float, int], float, int) -> None
        """Adds a small signal transistor model to the circuit.

        Parameters
        ----------
        d_name : str
            drain net name.
        g_name : str
            gate net name.
        s_name : str
            source net name.
        b_name : str
            body net name.  Defaults to 'gnd'.
        mos_type : str
            transistor type.  Either 'nch' or 'pch'.
        intent : str
            transistor threshold flavor.
        w : Union[float, int]
            transistor width.
        lch : float
            transistor channel length.
        fg : int
            transistor number of fingers.
        """
        node_d = self._get_node_id(d_name)
        node_g = self._get_node_id(g_name)
        node_s = self._get_node_id(s_name)
        node_b = self._get_node_id(b_name)

        # get existing current function.  Initalize if not found.
        ids_key = (mos_type, intent, lch)
        if ids_key in self._transistors:
            arow, acol, bdata, fg_list, ds_list = self._transistors[ids_key]
        else:
            arow, acol, bdata, fg_list, ds_list = [], [], [], [], []
            self._transistors[ids_key] = (arow, acol, bdata, fg_list, ds_list)

        # record Ai and bi data
        offset = len(fg_list) * 4
        arow.extend([offset + 1, offset + 1, offset + 2, offset + 2, offset + 3, offset + 3])
        acol.extend([node_b, node_s, node_d, node_s, node_g, node_s])
        bdata.append(w)
        fg_list.append(fg)
        ds_list.append((node_d, node_s))

    def solve(self, env, guess_dict, itol=1e-10, inorm=1e-6):
        # type: (str, Dict[str, float], float, float) -> Dict[str, float]
        """Solve DC operating point.

        Parameters
        ----------
        env : str
            the simulation environment.
        guess_dict : Dict[str, float]
            initial guess dictionary.
        itol : float
            current error tolerance.
        inorm : float
            current normalization factor.

        Returns
        -------
        op_dict : Dict[str, float]
            DC operating point dictionary.
        """
        # step 1: get list of nodes to solve
        node_list = [idx for idx in range(self._n) if idx not in self._node_voltage]
        reverse_dict = {nid: idx for idx, nid in enumerate(node_list)}
        ndim = len(node_list)

        # step 2: get Av and bv
        amatv = scipy.sparse.csr_matrix(([1] * ndim, (node_list, np.arange(ndim))), shape=(self._n, ndim))
        bmatv = np.zeros(self._n)
        for nid, val in self._node_voltage.items():
            bmatv[nid] = val

        # step 3: gather current functions, and output matrix entries
        ifun_list = []
        out_data = []
        out_row = []
        out_col = []
        out_col_cnt = 0
        for (mos_type, intent, lch), (arow, acol, bdata, fg_list, ds_list) in self._transistors.items():
            db = self._ndb if mos_type == 'nch' else self._pdb
            ifun = db.get_function('ids', env=env, intent=intent, l=lch)
            # step 3A: compute Ai and bi
            num_tran = len(fg_list)
            adata = [1, -1] * (3 * num_tran)
            amati = scipy.sparse.csr_matrix((adata, (arow, acol)), shape=(4 * num_tran, self._n))
            bmati = np.zeros(4 * num_tran)
            bmati[0::4] = bdata

            # step 3B: compute A = Ai * Av, b = Ai * bv + bi
            amat = amati.dot(amatv)
            bmat = amati.dot(bmatv) + bmati
            # record scale matrix and function.
            scale_mat = scipy.sparse.diags(fg_list) / inorm
            ifun_list.append((ifun, scale_mat, amat, bmat))
            for node_d, node_s in ds_list:
                if node_d in reverse_dict:
                    out_row.append(reverse_dict[node_d])
                    out_data.append(-1)
                    out_col.append(out_col_cnt)
                if node_s in reverse_dict:
                    out_row.append(reverse_dict[node_s])
                    out_data.append(1)
                    out_col.append(out_col_cnt)
                out_col_cnt += 1
        # construct output matrix
        out_mat = scipy.sparse.csr_matrix((out_data, (out_row, out_col)), shape=(ndim, out_col_cnt))

        # step 4: define zero function
        def zero_fun(varr):
            iarr = np.empty(out_col_cnt)
            offset = 0
            for idsf, smat, ai, bi in ifun_list:
                num_out = smat.shape[0]
                # reshape going row first instead of column
                arg = (ai.dot(varr) + bi).reshape(4, -1, order='F').T
                if idsf.ndim == 3:
                    # handle case where transistor source and body are shorted
                    tmpval = idsf(arg[:, [0, 2, 3]])
                else:
                    tmpval = idsf(arg)
                iarr[offset:offset + num_out] = smat.dot(tmpval)
                offset += num_out
            return out_mat.dot(iarr)

        # step 5: define zero function
        def jac_fun(varr):
            jarr = np.empty((out_col_cnt, ndim))
            offset = 0
            for idsf, smat, ai, bi in ifun_list:
                num_out = smat.shape[0]
                # reshape going row first instead of column
                arg = (ai.dot(varr) + bi).reshape(4, -1, order='F').T
                if idsf.ndim == 3:
                    # handle case where transistor source and body are shorted
                    tmpval = idsf.jacobian(arg[:, [0, 2, 3]])
                    # noinspection PyTypeChecker
                    tmpval = np.insert(tmpval, 1, 0.0, axis=len(tmpval.shape) - 1)
                else:
                    tmpval = idsf.jacobian(arg)
                jcur = smat.dot(tmpval)
                for idx in range(num_out):
                    # ai is sparse matrix; multiplication is matrix
                    jarr[offset + idx, :] = jcur[idx, :] @ ai[4 * idx:4 * idx + 4, :]
                offset += num_out
            return out_mat.dot(jarr)

        xguess = np.empty(ndim)
        for name, guess_val in guess_dict.items():
            xguess[reverse_dict[self._node_id[name]]] = guess_val

        result = scipy.optimize.root(zero_fun, xguess, jac=jac_fun, tol=itol / inorm, method='hybr')
        if not result.success:
            raise ValueError('solution failed.')

        op_dict = {self._node_name_lookup[nid]: result.x[idx] for idx, nid in enumerate(node_list)}
        return op_dict


================================================
FILE: bag/data/digital.py
================================================
# -*- coding: utf-8 -*-

"""This module defines functions useful for digital verification/postprocessing.
"""

from typing import Optional, List, Tuple

import numpy as np

from .core import Waveform


def de_bruijn(n, symbols=None):
    # type: (int, Optional[List[float]]) -> List[float]
    """Returns a De Bruijn sequence with subsequence of length n.

    a De Bruijn sequence with subsequence of length n is a sequence such that
    all possible subsequences of length n appear exactly once somewhere in the
    sequence.  This method is useful for simulating the worst case eye diagram
    given finite impulse response.

    Parameters
    ----------
    n : int
        length of the subsequence.
    symbols : Optional[List[float]] or None
        the list of symbols.  If None, defaults to [0.0, 1.0].

    Returns
    -------
    seq : List[float]
        the de bruijn sequence.
    """
    symbols = symbols or [0.0, 1.0]
    k = len(symbols)

    a = [0] * (k * n)
    sequence = []

    def db(t, p):
        if t > n:
            if n % p == 0:
                sequence.extend(a[1:p + 1])
        else:
            a[t] = a[t - p]
            db(t + 1, p)
            for j in range(a[t - p] + 1, k):
                a[t] = j
                db(t + 1, t)

    db(1, 1)
    return [symbols[i] for i in sequence]


def dig_to_pwl(values, tper, trf, td=0):
    # type: (List[float], float, float, float) -> Tuple[List[float], List[float]]
    """Convert a list of digital bits to PWL waveform.

    This function supports negative delay.  However, time/value pairs for negative data
    are truncated.

    Parameters
    ----------
    values : List[float]
        list of values for each bit.
    tper : float
        the period in seconds.
    trf : float
        the rise/fall time in seconds.
    td : float
        the delay

    Returns
    -------
    tvec : List[float]
        the time vector.
    yvec : List[float]
        the value vector.
    """
    y0 = values[0]
    tcur, ycur = td, y0
    tvec, yvec = [], []
    for v in values:
        if v != ycur:
            if tcur >= 0:
                tvec.append(tcur)
                yvec.append(ycur)
            elif tcur < 0 < tcur + trf:
                # make sure time starts at 0
                tvec.append(0)
                yvec.append(ycur - (v - ycur) / trf * tcur)
            ycur = v
            if tcur + trf >= 0:
                tvec.append(tcur + trf)
                yvec.append(ycur)
            elif tcur + trf < 0 < tcur + tper:
                # make sure time starts at 0
                tvec.append(0)
                yvec.append(ycur)
            tcur += tper
        else:
            if tcur <= 0 < tcur + tper:
                # make sure time starts at 0
                tvec.append(0)
                yvec.append(ycur)
            tcur += tper

    if not tvec:
        # only here if input is constant
        tvec = [0, tper]
        yvec = [y0, y0]
    elif tvec[0] > 0:
        # make time start at 0
        tvec.insert(0, 0)
        yvec.insert(0, y0)

    return tvec, yvec


def get_crossing_index(yvec, threshold, n=0, rising=True):
    # type: (np.array, float, int, bool) -> int
    """Returns the first index that the given numpy array crosses the given threshold.

    Parameters
    ----------
    yvec : np.array
        the numpy array.
    threshold : float
        the crossing threshold.
    n : int
        returns the nth edge index, with n=0 being the first index.
    rising : bool
        True to return rising edge index.  False to return falling edge index.

    Returns
    -------
    idx : int
        the crossing edge index.
    """

    bool_vec = yvec >= threshold
    qvec = bool_vec.astype(int)
    dvec = np.diff(qvec)

    dvec = np.maximum(dvec, 0) if rising else np.minimum(dvec, 0)
    idx_list = dvec.nonzero()[0]
    return idx_list[n]


def get_flop_timing(tvec, d, q, clk, ttol, data_thres=0.5,
                    clk_thres=0.5, tstart=0.0, clk_edge='rising', tag=None, invert=False):
    """Calculate flop timing parameters given the associated waveforms.

    This function performs the following steps:

    1. find all valid clock edges.  Compute period of the clock (clock waveform
       must be periodic).
    
    2. For each valid clock edge:

        A. Check if the input changes in the previous cycle.  If so, compute tsetup.
           Otherwise, tsetup = tperiod.
    
        B. Check if input changes in the current cycle.  If so, compute thold.
           Otherwise, thold = tperiod.
  
        C. Check that output transition at most once and that output = input.
           Otherwise, record an error.

        D. record the output data polarity.

    3. For each output data polarity, compute the minimum tsetup and thold and any
       errors.  Return summary as a dictionary.

    
    The output is a dictionary with keys 'setup', 'hold', 'delay', and 'errors'.
    the setup/hold/delay entries contains 2-element tuples describing the worst
    setup/hold/delay time.  The first element is the setup/hold/delay time, and
    the second element is the clock edge time at which it occurs.  The errors field
    stores all clock edge times at which an error occurs.


    Parameters
    ----------
    tvec : np.ndarray
        the time data.
    d : np.ndarray
        the input data.
    q : np.ndarray
        the output data.
    clk : np.ndarray
        the clock data.
    ttol : float
        time resolution.
    data_thres : float
        the data threshold.
    clk_thres : float
        the clock threshold.
    tstart : float
        ignore data points before tstart.
    clk_edge : str
        the clock edge type.  Valid values are "rising", "falling", or "both".
    tag : obj
        an identifier tag to append to results.
    invert : bool
        if True, the flop output is inverted from the data.

    Returns
    -------
    data : dict[str, any]
        A dictionary describing the worst setup/hold/delay and errors, if any.
    """
    d_wv = Waveform(tvec, d, ttol)
    clk_wv = Waveform(tvec, clk, ttol)
    q_wv = Waveform(tvec, q, ttol)
    tend = tvec[-1]

    # get all clock sampling times and clock period
    samp_times = clk_wv.get_all_crossings(clk_thres, start=tstart, edge=clk_edge)
    tper = (samp_times[-1] - samp_times[0]) / (len(samp_times) - 1)
    # ignore last clock cycle if it's not a full cycle.
    if samp_times[-1] + tper > tend:
        samp_times = samp_times[:-1]

    # compute setup/hold/error for each clock period
    data = {'setup': (tper, -1), 'hold': (tper, -1), 'delay': (0.0, -1), 'errors': []}
    for t in samp_times:
        d_prev = d_wv.get_all_crossings(data_thres, start=t - tper, stop=t, edge='both')
        d_cur = d_wv.get_all_crossings(data_thres, start=t, stop=t + tper, edge='both')
        q_cur = q_wv.get_all_crossings(data_thres, start=t, stop=t + tper, edge='both')
        d_val = d_wv(t) > data_thres
        q_val = q_wv(t + tper) > data_thres

        # calculate setup/hold/delay
        tsetup = t - d_prev[-1] if d_prev else tper
        thold = d_cur[0] - t if d_cur else tper
        tdelay = q_cur[0] - t if q_cur else 0.0

        # check if flop has error
        error = (invert != (q_val != d_val)) or (len(q_cur) > 1)

        # record results
        if tsetup < data['setup'][0]:
            data['setup'] = (tsetup, t)
        if thold < data['hold'][0]:
            data['hold'] = (thold, t)
        if tdelay > data['delay'][0]:
            data['delay'] = (tdelay, t)
        if error:
            data['errors'].append(t)

    if tag is not None:
        data['setup'] += (tag, )
        data['hold'] += (tag, )
        data['delay'] += (tag, )
        data['errors'] = [(t, tag) for t in data['errors']]

    return data


================================================
FILE: bag/data/lti.py
================================================
# -*- coding: utf-8 -*-

"""This module defines functions and classes useful for characterizing linear time-invariant circuits.
"""

from typing import Dict, List, Tuple, Union, Optional

import numpy as np
import scipy.signal
import scipy.sparse
import scipy.sparse.linalg
# noinspection PyProtectedMember
from scipy.signal.ltisys import StateSpaceContinuous, TransferFunctionContinuous


class LTICircuit(object):
    """A class that models a linear-time-invariant circuit.

    This class computes AC transfer functions for linear-time-invariant circuits.

    Note: Since this class work with AC transfer functions, 'gnd' in this circuit is AC ground.

    Parameters
    ----------
    udot_tol : float
        tolerance to determine if dependency on input derivatives is zero.
    """

    _float_min = np.finfo(np.float64).eps

    def __init__(self, udot_tol=1e-12):
        # type: (float) -> None
        self._num_n = 0
        self._gmat_data = {}  # type: Dict[Tuple[int, int], float]
        self._cmat_data = {}  # type: Dict[Tuple[int, int], float]
        self._vcvs_list = []  # type: List[Tuple[int, int, int, int, float]]
        self._ind_data = {}  # type: Dict[Tuple[int, int], float]
        self._node_id = {'gnd': -1}
        self._udot_tol = udot_tol

    def _get_node_id(self, name):
        # type: (str) -> int
        if name not in self._node_id:
            ans = self._num_n
            self._node_id[name] = ans
            self._num_n += 1
            return ans
        else:
            return self._node_id[name]

    @staticmethod
    def _add(mat, key, val):
        # type: (Dict[Tuple[int, int], float], Tuple[int, int], float) -> None
        if key in mat:
            mat[key] += val
        else:
            mat[key] = val

    def add_res(self, res, p_name, n_name):
        # type: (float, str, str) -> None
        """Adds a resistor to the circuit.

        Parameters
        ----------
        res : float
            the resistance value, in Ohms.
        p_name : str
            the positive terminal net name.
        n_name : str
            the negative terminal net name.
        """
        # avoid 0 resistance.
        res_sgn = 1 if res >= 0 else -1
        g = res_sgn / max(abs(res), self._float_min)
        self.add_conductance(g, p_name, n_name)

    def add_conductance(self, g, p_name, n_name):
        # type: (float, str, str) -> None
        """Adds a resistor to the circuit given conductance value.

        Parameters
        ----------
        g : float
            the conductance value, in inverse Ohms.
        p_name : str
            the positive terminal net name.
        n_name : str
            the negative terminal net name.
        """
        node_p = self._get_node_id(p_name)
        node_n = self._get_node_id(n_name)

        if node_p == node_n:
            return
        if node_p < node_n:
            node_p, node_n = node_n, node_p

        self._add(self._gmat_data, (node_p, node_p), g)
        if node_n >= 0:
            self._add(self._gmat_data, (node_p, node_n), -g)
            self._add(self._gmat_data, (node_n, node_p), -g)
            self._add(self._gmat_data, (node_n, node_n), g)

    def add_vccs(self, gm, p_name, n_name, cp_name, cn_name='gnd'):
        # type: (float, str, str, str, str) -> None
        """Adds a voltage controlled current source to the circuit.

        Parameters
        ----------
        gm : float
            the gain of the voltage controlled current source, in Siemens.
        p_name : str
            the terminal that the current flows out of.
        n_name : str
            the terminal that the current flows in to.
        cp_name : str
            the positive voltage control terminal.
        cn_name : str
            the negative voltage control terminal.  Defaults to 'gnd'.
        """
        node_p = self._get_node_id(p_name)
        node_n = self._get_node_id(n_name)
        node_cp = self._get_node_id(cp_name)
        node_cn = self._get_node_id(cn_name)

        if node_p == node_n or node_cp == node_cn:
            return

        if node_cp >= 0:
            if node_p >= 0:
                self._add(self._gmat_data, (node_p, node_cp), gm)
            if node_n >= 0:
                self._add(self._gmat_data, (node_n, node_cp), -gm)
        if node_cn >= 0:
            if node_p >= 0:
                self._add(self._gmat_data, (node_p, node_cn), -gm)
            if node_n >= 0:
                self._add(self._gmat_data, (node_n, node_cn), gm)

    def add_vcvs(self, gain, p_name, n_name, cp_name, cn_name='gnd'):
        # type: (float, str, str, str, str) -> None
        """Adds a voltage controlled voltage source to the circuit.

        Parameters
        ----------
        gain : float
            the gain of the voltage controlled voltage source.
        p_name : str
            the positive terminal of the output voltage source.
        n_name : str
            the negative terminal of the output voltage source.
        cp_name : str
            the positive voltage control terminal.
        cn_name : str
            the negative voltage control terminal.  Defaults to 'gnd'.
        """
        node_p = self._get_node_id(p_name)
        node_n = self._get_node_id(n_name)
        node_cp = self._get_node_id(cp_name)
        node_cn = self._get_node_id(cn_name)

        if node_p == node_n:
            raise ValueError('positive and negative terminal of a vcvs cannot be the same.')
        if node_cp == node_cn:
            raise ValueError('positive and negative control terminal of a vcvs cannot be the same.')
        if node_p < node_n:
            # flip nodes so we always have node_p > node_n, to guarantee node_p >= 0
            node_p, node_n, node_cp, node_cn = node_n, node_p, node_cn, node_cp

        self._vcvs_list.append((node_p, node_n, node_cp, node_cn, gain))

    def add_cap(self, cap, p_name, n_name):
        # type: (float, str, str) -> None
        """Adds a capacitor to the circuit.

        Parameters
        ----------
        cap : float
            the capacitance value, in Farads.
        p_name : str
            the positive terminal net name.
        n_name : str
            the negative terminal net name.
        """
        node_p = self._get_node_id(p_name)
        node_n = self._get_node_id(n_name)

        if node_p == node_n:
            return
        if node_p < node_n:
            node_p, node_n = node_n, node_p

        self._add(self._cmat_data, (node_p, node_p), cap)
        if node_n >= 0:
            self._add(self._cmat_data, (node_p, node_n), -cap)
            self._add(self._cmat_data, (node_n, node_p), -cap)
            self._add(self._cmat_data, (node_n, node_n), cap)

    def add_ind(self, ind, p_name, n_name):
        # type: (float, str, str) -> None
        """Adds an inductor to the circuit.

        Parameters
        ----------
        ind : float
            the inductance value, in Henries.
        p_name : str
            the positive terminal net name.
        n_name : str
            the negative terminal net name.
        """
        node_p = self._get_node_id(p_name)
        node_n = self._get_node_id(n_name)

        if node_p == node_n:
            return
        if node_p < node_n:
            key = node_n, node_p
        else:
            key = node_p, node_n

        if key not in self._ind_data:
            self._ind_data[key] = ind
        else:
            self._ind_data[key] = 1.0 / (1.0 / ind + 1.0 / self._ind_data[key])

    def add_transistor(self, tran_info, d_name, g_name, s_name, b_name='gnd', fg=1, neg_cap=True):
        # type: (Dict[str, float], str, str, str, str, Union[float, int], bool) -> None
        """Adds a small signal transistor model to the circuit.

        Parameters
        ----------
        tran_info : Dict[str, float]
            a dictionary of 1-finger transistor small signal parameters.  Should contain gm, gds, gb,
            cgd, cgs, cgb, cds, cdb, and csb.
        d_name : str
            drain net name.
        g_name : str
            gate net name.
        s_name : str
            source net name.
        b_name : str
            body net name.  Defaults to 'gnd'.
        fg : Union[float, int]
            number of transistor fingers.
        neg_cap : bool
            True to allow negative capacitance (which is there due to model fitting).
        """
        gm = tran_info['gm'] * fg
        gds = tran_info['gds'] * fg
        cgd = tran_info['cgd'] * fg
        cgs = tran_info['cgs'] * fg
        cds = tran_info['cds'] * fg
        cgb = tran_info.get('cgb', 0) * fg
        cdb = tran_info.get('cdb', 0) * fg
        csb = tran_info.get('csb', 0) * fg

        if not neg_cap:
            cgd = max(cgd, 0)
            cgs = max(cgs, 0)
            cds = max(cds, 0)
            cgb = max(cgb, 0)
            cdb = max(cdb, 0)
            csb = max(csb, 0)

        self.add_vccs(gm, d_name, s_name, g_name, s_name)
        self.add_conductance(gds, d_name, s_name)
        self.add_cap(cgd, g_name, d_name)
        self.add_cap(cgs, g_name, s_name)
        self.add_cap(cds, d_name, s_name)
        self.add_cap(cgb, g_name, b_name)
        self.add_cap(cdb, d_name, b_name)
        self.add_cap(csb, s_name, b_name)

        if 'gb' in tran_info:
            # only add these if source is not shorted to body.
            gb = tran_info['gb'] * fg
            self.add_vccs(gb, d_name, s_name, b_name, s_name)

    @classmethod
    def _count_rank(cls, diag):
        # type: (np.ndarray) -> int
        diag_abs = np.abs(diag)
        float_min = cls._float_min
        rank_tol = diag_abs[0] * diag.size * float_min
        rank_cnt = diag_abs > rank_tol  # type: np.ndarray
        return np.count_nonzero(rank_cnt)

    @classmethod
    def _solve_gx_bw(cls, g, b):
        # type: (np.ndarray, np.ndarray) -> Tuple[np.ndarray, np.ndarray]
        """Solve the equation G*x + B*[w, w', ...].T = 0 for x.

        Finds matrix Ka, Kw such that x = Ka * a + Kw * [w, w', ...].T solves
        the given equation for any value of a.

        Parameters
        ----------
        g : np.ndarray
            the G matrix, with shape (M, N) and M < N.
        b : np.ndarray
            the B matrix.

        Returns
        -------
        ka : np.ndarray
            the Ky matrix.
        kw : np.ndarray
            the Kw matrix.
        """
        # G = U*S*Vh
        u, s, vh = scipy.linalg.svd(g, full_matrices=True, overwrite_a=True)
        # let B=Uh*B, so now S*Vh*x + B*w = 0
        b = u.T.dot(b)
        # let y = Vh*x, or x = V*y, so now S*y + U*B*w = 0
        v = vh.T
        # truncate the bottom 0 part of S, now S_top*y_top + B_top*w = 0
        rank = cls._count_rank(s)
        # check bottom part of B.  If not 0, there's no solution
        b_abs = np.abs(b)
        zero_tol = np.amax(b_abs) * cls._float_min
        if np.count_nonzero(b_abs[rank:, :] > zero_tol) > 0:
            raise ValueError('B matrix bottom is not zero.  This circuit has no solution.')
        b_top = b[:rank, :]
        s_top_inv = 1 / s[:rank]  # type: np.ndarray
        s_top_inv = np.diag(s_top_inv)
        # solving, we get y_top = -S_top^-1*B_top*w = Ku*w
        kw = s_top_inv.dot(-b_top)
        # now x = V*y = Vl*y_top + Vr*y_bot = Vr*y_bot + Vl*Kw*w = Ky*y_bot = Kw*w
        vl = v[:, :rank]
        vr = v[:, rank:]
        kw = vl.dot(kw)
        return vr, kw

    @classmethod
    def _transform_c_qr(cls, g, c, b, d):
        """Reveal redundant variables by transforming C matrix using QR decomposition"""
        q, r, p = scipy.linalg.qr(c, pivoting=True)
        rank = cls._count_rank(np.diag(r))
        qh = q.T
        return rank, qh.dot(g[:, p]), r, qh.dot(b), d[:, p]

    # @classmethod
    # def _transform_c_svd(cls, g, c, b, d):
    #     """Reveal redundant variables by transforming C matrix using SVD decomposition"""
    #     u, s, vh = scipy.linalg.svd(c, full_matrices=True, overwrite_a=True)
    #     uh = u.T
    #     v = vh.T
    #     rank = cls._count_rank(s)
    #     return rank, uh.dot(g).dot(v), np.diag(s), uh.dot(b), d.dot(v)

    @classmethod
    def _reduce_state_space(cls, g, c, b, d, e, ndim_w):
        """Reduce state space variables.

        Given the state equation G*x + C*x' + B*[w, w', w'', ...].T = 0, and
        y = D*x + E*[w, w', w'', ...].T, check if C is full rank.  If not,
        we compute new G, C, and B matrices with reduced dimensions.
        """
        # step 0: transform C and obtain rank
        rank, g, c, b, d = cls._transform_c_qr(g, c, b, d)
        # rank, g, c, b, d = cls._transform_c_svd(g, c, b, d)
        while rank < c.shape[0]:
            # step 1: eliminate x' term by looking at bottom part of matrices
            ctop = c[:rank, :]
            gtop = g[:rank, :]
            gbot = g[rank:, :]
            btop = b[:rank, :]
            bbot = b[rank:, :]
            # step 2: find ka and kw from bottom
            ka, kw = cls._solve_gx_bw(gbot, bbot)
            # step 3: substitute x = ka * a + kw * [w, w', w'', ...].T
            g = gtop.dot(ka)
            c = ctop.dot(ka)
            b = np.zeros((btop.shape[0], btop.shape[1] + ndim_w))
            b[:, :btop.shape[1]] = btop + gtop.dot(kw)
            b[:, ndim_w:] += ctop.dot(kw)
            enew = np.zeros((e.shape[0], e.shape[1] + ndim_w))
            enew[:, :-ndim_w] = e + d.dot(kw)
            e = enew
            d = d.dot(ka)
            # step 4: transform C to prepare for next iteration
            rank, g, c, b, d = cls._transform_c_qr(g, c, b, d)
            # rank, g, c, b, d = cls._transform_c_svd(g, c, b, d)

        g, c, b, d, e = cls._simplify(g, c, b, d, e, ndim_w)
        return g, c, b, d, e

    @classmethod
    def _simplify(cls, g, c, b, d, e, ndim_w):
        """Eliminate input derivatives by re-defining state variables.
        """
        while b.shape[1] > ndim_w:
            kw = scipy.linalg.solve_triangular(c, b[:, ndim_w:])
            bnew = np.dot(g, -kw)
            bnew[:, :ndim_w] += b[:, :ndim_w]
            b = bnew
            e[:, :kw.shape[1]] -= d.dot(kw)
        return g, c, b, d, e

    def _build_mna_matrices(self, inputs, outputs, in_type='v'):
        # type: (Union[str, List[str]], Union[str, List[str]], str) -> Tuple[np.ndarray, ...]
        """Create and return MNA matrices representing this circuit.

        Parameters
        ----------
        inputs : Union[str, List[str]]
            the input voltage/current node name(s).
        outputs : Union[str, List[str]]
            the output voltage node name(s).
        in_type : str
            set to 'v' for input voltage sources.  Otherwise, current sources.

        Returns
        -------
        g : np.ndarray
            the conductance matrix
        c : np.ndarray
            the capacitance/inductance matrix.
        b : np.ndarray
            the input-to-state matrix.
        d : np.ndarray
            the state-to-output matrix.
        e : np.ndarray
            the input-to-output matrix.
        """
        if isinstance(inputs, list) or isinstance(inputs, tuple):
            node_ins = [self._node_id[name] for name in inputs]
        else:
            node_ins = [self._node_id[inputs]]
        if isinstance(outputs, list) or isinstance(outputs, tuple):
            node_outs = [self._node_id[name] for name in outputs]
        else:
            node_outs = [self._node_id[outputs]]

        is_voltage = (in_type == 'v')

        # step 1: construct matrices
        gdata, grows, gcols = [], [], []
        cdata, crows, ccols = [], [], []
        # step 1A: gather conductors/vccs
        for (ridx, cidx), gval in self._gmat_data.items():
            gdata.append(gval)
            grows.append(ridx)
            gcols.append(cidx)
        # step 1B: gather capacitors
        for (ridx, cidx), cval in self._cmat_data.items():
            cdata.append(cval)
            crows.append(ridx)
            ccols.append(cidx)
        # step 1C: gather inductors
        num_states = self._num_n
        for (node_p, node_n), lval in self._ind_data.items():
            gdata.append(1)
            grows.append(node_p)
            gcols.append(num_states)
            gdata.append(1)
            grows.append(num_states)
            gcols.append(node_p)
            if node_n >= 0:
                gdata.append(-1)
                grows.append(node_n)
                gcols.append(num_states)
                gdata.append(-1)
                grows.append(num_states)
                gcols.append(node_n)
            cdata.append(-lval)
            crows.append(num_states)
            ccols.append(num_states)
            num_states += 1
        # step 1D: add currents from vcvs
        for node_p, node_n, node_cp, node_cn, gain in self._vcvs_list:
            # vcvs means vp - vn - A*vcp + A*vcn = 0, and current flows from p to n
            # current flowing out of p
            gdata.append(1)
            grows.append(node_p)
            gcols.append(num_states)
            # voltage of p
            gdata.append(1)
            grows.append(num_states)
            gcols.append(node_p)
            if node_n >= 0:
                # current flowing into n
                gdata.append(-1)
                grows.append(node_n)
                gcols.append(num_states)
                # voltage of n
                gdata.append(-1)
                grows.append(num_states)
                gcols.append(node_n)
            if node_cp >= 0:
                # voltage of cp
                gdata.append(-gain)
                grows.append(num_states)
                gcols.append(node_cp)
            if node_cn >= 0:
                # voltage of cn
                gdata.append(gain)
                grows.append(num_states)
                gcols.append(node_cn)
            num_states += 1

        ndim_in = len(node_ins)
        if is_voltage:
            # step 1E: add current/voltage from input voltage source
            b = np.zeros((num_states + ndim_in, ndim_in))
            for in_idx, node_in in enumerate(node_ins):
                gdata.append(1)
                grows.append(node_in)
                gcols.append(num_states)
                gdata.append(-1)
                grows.append(num_states)
                gcols.append(node_in)
                b[num_states + in_idx, in_idx] = 1
            num_states += ndim_in
        else:
            # inject current to node_in
            b = np.zeros((num_states, ndim_in))
            for in_idx, node_in in enumerate(node_ins):
                b[node_in, in_idx] = -1

        # step 2: create matrices
        shape = (num_states, num_states)
        g = scipy.sparse.csc_matrix((gdata, (grows, gcols)), shape=shape).todense().A
        c = scipy.sparse.csc_matrix((cdata, (crows, ccols)), shape=shape).todense().A
        ndim_out = len(node_outs)
        d = scipy.sparse.csc_matrix((np.ones(ndim_out), (np.arange(ndim_out), node_outs)),
                                    shape=(ndim_out, num_states)).todense().A
        e = np.zeros((ndim_out, ndim_in))

        return g, c, b, d, e

    def get_state_space(self, inputs, outputs, in_type='v'):
        # type: (Union[str, List[str]], Union[str, List[str]], str) -> StateSpaceContinuous
        """Compute the state space model from the given inputs to outputs.

        Parameters
        ----------
        inputs : Union[str, List[str]]
            the input voltage/current node name(s).
        outputs : Union[str, List[str]]
            the output voltage node name(s).
        in_type : str
            set to 'v' for input voltage sources.  Otherwise, current sources.

        Returns
        -------
        system : StateSpaceContinuous
            the scipy state space object.  See scipy.signal package on how to use this object.
        """
        g0, c0, b0, d0, e0 = self._build_mna_matrices(inputs, outputs, in_type)
        ndim_in = e0.shape[1]
        g, c, b, d, e = self._reduce_state_space(g0, c0, b0, d0, e0, ndim_in)
        amat = scipy.linalg.solve_triangular(c, -g)
        bmat = scipy.linalg.solve_triangular(c, -b)
        cmat = d
        e_abs = np.abs(e)
        tol = np.amax(e_abs) * self._udot_tol
        if np.count_nonzero(e_abs[:, ndim_in:] > tol) > 0:
            print('WARNING: output depends on input derivatives.  Ignored.')
            print('D matrix: ')
            print(e)
        dmat = e[:, :ndim_in]

        return StateSpaceContinuous(amat, bmat, cmat, dmat)

    def get_num_den(self, in_name, out_name, in_type='v', atol=0.0):
        # type: (str, str, str, float) -> Tuple[np.ndarray, np.ndarray]
        """Compute the transfer function between the two given nodes.

        Parameters
        ----------
        in_name : str
            the input voltage/current node name.
        out_name : Union[str, List[str]]
            the output voltage node name.
        in_type : str
            set to 'v' for input voltage sources.  Otherwise, current sources.
        atol : float
            absolute tolerance for checking zeros in the numerator.  Used to filter out scipy warnings.

        Returns
        -------
        num : np.ndarray
            the numerator polynomial.
        den : np.ndarray
            the denominator polynomial.
        """
        state_space = self.get_state_space(in_name, out_name, in_type=in_type)
        num, den = scipy.signal.ss2tf(state_space.A, state_space.B, state_space.C, state_space.D)
        num = num[0, :]
        # check if numerator has leading zeros.
        # this makes it so the user have full control over numerical precision, and
        # avoid scipy bad conditioning warnings.
        while abs(num[0]) <= atol:
            num = num[1:]

        return num, den

    def get_transfer_function(self, in_name, out_name, in_type='v', atol=0.0):
        # type: (str, str, str, float) -> TransferFunctionContinuous
        """Compute the transfer function between the two given nodes.

        Parameters
        ----------
        in_name : str
            the input voltage/current node name.
        out_name : Union[str, List[str]]
            the output voltage node name.
        in_type : str
            set to 'v' for input voltage sources.  Otherwise, current sources.
        atol : float
            absolute tolerance for checking zeros in the numerator.  Used to filter out scipy warnings.

        Returns
        -------
        system : TransferFunctionContinuous
            the scipy transfer function object.  See scipy.signal package on how to use this object.
        """
        num, den = self.get_num_den(in_name, out_name, in_type=in_type, atol=atol)
        return TransferFunctionContinuous(num, den)

    def get_impedance(self, node_name, freq, atol=0.0):
        # type: (str, float, float) -> complex
        """Computes the impedance looking into the given node.

        Parameters
        ----------
        node_name : str
            the node to compute impedance for.  We will inject a current into this node and measure the voltage
            on this node.
        freq : float
            the frequency to compute the impedance at, in Hertz.
        atol : float
            absolute tolerance for checking zeros in the numerator.  Used to filter out scipy warnings.

        Returns
        -------
        impedance : complex
            the impedance value, in Ohms.
        """
        sys = self.get_transfer_function(node_name, node_name, in_type='i', atol=atol)
        w_test = 2 * np.pi * freq
        _, zin_vec = sys.freqresp(w=[w_test])
        return zin_vec[0]


def get_w_crossings(num, den, atol=1e-8):
    # type: (np.multiarray.ndarray, np.multiarray.ndarray, float) -> Tuple[Optional[float], Optional[float]]
    """Given the numerator and denominator of the transfer function, compute gain margin/phase margin frequencies.

    To determine the crossover frequencies, we write the transfer function as:

    .. math::

        \\frac{A(w) + jB(w)}{C(w) + jD(w)}

    where :math:`A(w)`, :math:`B(w)`, :math:`C(w)`, and :math:`D(w)` are real polynomials.  The gain margin frequency
    is the frequency at which:

    .. math::

        \\frac{B(w)}{A(w)} = \\frac{D(w)}{C(w)} \\implies A(w)D(w) - B(w)C(w) = 0


    The phase margin frequency is the frequency at which:

    .. math::

        \\frac{A^2(w) + B^2(w)}{C^2(w) + D^2(w)} = 1 \implies A^2(w) + B^2(w) - C^2(w) - D^2(w) = 0

    This function solves these two equations and returns the smallest real and positive roots.

    Parameters
    ----------
    num : np.multiarray.ndarray
        the numerator polynomial coefficients array.  index 0 is coefficient for highest term.
    den : np.multiarray.ndarray
        the denominator polynomial coefficients array.  index 0 is coefficient for highest term.
    atol : float
        absolute tolerance used to check if the imaginary part of a root is 0, or if a root is greater than 0.

    Returns
    -------
    w_phase : Optional[float]
        lowest positive frequency in rad/s at which the gain becomes unity.  None if no such frequency exist.
    w_gain : Optional[float]
        lower positive frequency in rad/s at which the phase becomes 180 degrees.  None if no such frequency exist.
    """
    # construct A(w), B(w), C(w), and D(w)
    num_flip = num[::-1]
    den_flip = den[::-1]
    avec = np.copy(num_flip)
    bvec = np.copy(num_flip)
    cvec = np.copy(den_flip)
    dvec = np.copy(den_flip)
    avec[1::2] = 0
    avec[2::4] *= -1
    bvec[0::2] = 0
    bvec[3::4] *= -1
    cvec[1::2] = 0
    cvec[2::4] *= -1
    dvec[0::2] = 0
    dvec[3::4] *= -1

    apoly = np.poly1d(avec[::-1])
    bpoly = np.poly1d(bvec[::-1])
    cpoly = np.poly1d(cvec[::-1])
    dpoly = np.poly1d(dvec[::-1])

    # solve for w_phase/w_gain
    poly_list = [apoly**2 + bpoly**2 - cpoly**2 - dpoly**2,
                 apoly * dpoly - bpoly * cpoly]

    w_list = [None, None]  # type: List[Optional[float]]
    for idx in range(2):
        for root in poly_list[idx].roots:
            root_real = float(root.real)
            if abs(root.imag) < atol < root_real:
                w_list_idx = w_list[idx]
                if w_list_idx is None or root_real < w_list_idx:
                        w_list[idx] = root_real

    return w_list[0], w_list[1]


def get_w_3db(num, den, atol=1e-8):
    # type: (np.multiarray.ndarray, np.multiarray.ndarray, float) -> Optional[float]
    """Given the numerator and denominator of the transfer function, compute the 3dB frequency.

    To determine the 3dB frequency, we first normalize the transfer function so that its DC gain is one,
    then we write the transfer function as:

    .. math::

        \\frac{A(w) + jB(w)}{C(w) + jD(w)}

    where :math:`A(w)`, :math:`B(w)`, :math:`C(w)`, and :math:`D(w)` are real polynomials.  The 3dB frequency
    is the frequency at which:

    .. math::

        \\frac{A^2(w) + B^2(w)}{C^2(w) + D^2(w)} = 0.5 \implies A^2(w) + B^2(w) - 0.5\\left(C^2(w) + D^2(w)\\right) = 0

    This function solves this equation and returns the smallest real and positive roots.

    Parameters
    ----------
    num : np.multiarray.ndarray
        the numerator polynomial coefficients array.  index 0 is coefficient for highest term.
    den : np.multiarray.ndarray
        the denominator polynomial coefficients array.  index 0 is coefficient for highest term.
    atol : float
        absolute tolerance used to check if the imaginary part of a root is 0, or if a root is greater than 0.

    Returns
    -------
    w_3db : Optional[float]
        the 3dB frequency in rad/s.  None if no such frequency exist.
    """
    # construct A(w), B(w), C(w), and D(w) of normalized transfer function
    num_flip = num[::-1] / num[-1]
    den_flip = den[::-1] / den[-1]
    avec = np.copy(num_flip)
    bvec = np.copy(num_flip)
    cvec = np.copy(den_flip)
    dvec = np.copy(den_flip)
    avec[1::2] = 0
    avec[2::4] *= -1
    bvec[0::2] = 0
    bvec[3::4] *= -1
    cvec[1::2] = 0
    cvec[2::4] *= -1
    dvec[0::2] = 0
    dvec[3::4] *= -1

    apoly = np.poly1d(avec[::-1])
    bpoly = np.poly1d(bvec[::-1])
    cpoly = np.poly1d(cvec[::-1])
    dpoly = np.poly1d(dvec[::-1])

    # solve for w_phase/w_gain
    poly = apoly**2 + bpoly**2 - (cpoly**2 + dpoly**2) / 2  # type: np.poly1d
    w_ans = None
    for root in poly.roots:
        root_real = float(root.real)
        if abs(root.imag) < atol < root_real and (w_ans is None or root_real < w_ans):
            w_ans = root_real

    return w_ans


def get_stability_margins(num, den, rtol=1e-8, atol=1e-8):
    # type: (np.multiarray.ndarray, np.multiarray.ndarray, float, float) -> Tuple[float, float]
    """Given the numerator and denominator of the transfer function, compute phase and gain margins.

    Parameters
    ----------
    num : np.multiarray.ndarray
        the numerator polynomial coefficients array.  index 0 is coefficient for highest term.
    den : np.multiarray.ndarray
        the denominator polynomial coefficients array.  index 0 is coefficient for highest term.
    rtol : float
        relative tolerance.  Used to check if two frequencies are equal.
    atol : float
        absolute tolerance.  Used to check a number is equal to 0.

    Returns
    -------
    phase_margin : float
        the phase margin in degrees. If the system is unstable, a negative number is returned.
    gain_margin : float
        the gain margin.
    """
    poly_n = np.poly1d(num)
    poly_d = np.poly1d(den)

    # compute gain margin.
    w_phase, w_gain = get_w_crossings(num, den, atol=atol)
    if w_gain is None:
        gain_margin = float('inf')
    else:
        gain_margin = abs(poly_d(1j * w_gain) / poly_n(1j * w_gain))

    # compute phase margin
    if w_phase is None:
        # gain never equal to 1.  That means gain is always greater than 1 or gain is always less than 1.
        dc_gain = poly_n(0) / poly_d(0)
        if dc_gain < 1 - max(rtol, atol):
            # gain is always less than 1, infinite phase margin
            phase_margin = float('inf')
        else:
            # gain is always greater than 1, unstable
            phase_margin = -1
    elif w_gain is not None and w_phase > w_gain + max(w_gain * rtol, atol):
        # unity gain frequency > 180 degree frequency, we're unstable
        phase_margin = -1
    else:
        phase_margin = np.angle(poly_n(1j * w_phase) / poly_d(1j * w_phase), deg=True) + 180

    return phase_margin, gain_margin


================================================
FILE: bag/data/ltv.py
================================================
# -*- coding: utf-8 -*-

"""This module defines functions and classes for linear time-varying circuits data post-processing.
"""

import numpy as np
import scipy.interpolate as interp
import scipy.sparse as sparse


def _even_quotient(a, b, tol=1e-6):
    """Returns a / b if it is an integer, -1 if it is not.."""
    num = int(round(a / b))
    if abs(a - b * num) < abs(b * tol):
        return num
    return -1


class LTVImpulseFinite(object):
    r"""A class that computes finite impulse response of a linear time-varying circuit.

    This class computes the time-varying impulse response based on PSS/PAC simulation
    data, and provides several useful query methods.  Your simulation should be set up
    as follows:

    #. Setup PSS as usual.  We will denote system period as tper and fc = 1/tper.

    #. In PAC, set the maxmimum sidebands to m.

    #. In PAC, set the input frequency sweep to be absolute, and sweep from 0 to
       n * fstep in steps of fstep, where fstep = fc / k for some integer k.

       k should be chosen so that the output settles back to 0 after time k * tper.  k
       should also be chosen such that fstep is a nice round frequency.  Otherwise,
       numerical errors may introduce strange results.

       n should be chosen so that n * fstep is sufficiently large compared to system
       bandwidth.

    #. In PAC options, set the freqaxis option to be "in".

    #. After simulation, PAC should save the output frequency response as a function of
       output harmonic number and input frequency.  Post-process this into a complex 2D
       matrix hmat with shape (2 * m + 1, n + 1), and pass it to this class's constructor.

    Parameters
    ----------
    hmat : np.ndarray
        the PAC simulation data matrix with shape (2 * m + 1, n + 1).
        hmat[a + m, b] is the complex AC gain from input frequency b * fc / k
        to output frequency a * fc + b * fc / k.
    m : int
        number of output sidebands.
    n : int
        number of input frequencies.
    tper : float
        the system period, in seconds.
    k : int
        the ratio between period of the input impulse train and the system period.
        Must be an integer.
    out0 : :class:`numpy.ndarray`
        steady-state output transient waveform with 0 input over 1 period.  This should
        be a two-column array, where the first column is time vector and second column
        is the output.  Used to compute transient response.

    Notes
    -----
    This class uses the algorithm described in [1]_ to compute impulse response from PSS/PAC
    simulation data.  The impulse response :math:`h(t, \tau)` satisfies the following equation:

    .. math:: y(t) = \int_{-\infty}^{\infty} h(t, \tau) \cdot x(\tau)\ d\tau

    Intuitively, :math:`h(t, \tau)` represents the output at time :math:`t` subject to
    an impulse at time :math:`\tau`.  As described in the paper, If :math:`w_c` is the system
    frequency, and :math:`H_m(jw)` is the frequency response of the system at :math:`mw_c + w`
    due to an input sinusoid with frequency :math:`w`, then the impulse response can be calculated as:

    .. math::

        h(t, \tau) = \frac{1}{kT}\sum_{n=-\infty}^{\infty}\sum_{m=-\infty}^{\infty}
        H_m\left (j\dfrac{nw_c}{k}\right) \exp \left[ jmw_ct + j\dfrac{nw_c}{k} (t - \tau)\right]

    where :math:`0 \le \tau < T` and :math:`\tau \le t \le \tau + kT`.

    References
    ----------
    .. [1] J. Kim, B. S. Leibowitz and M. Jeeradit, "Impulse sensitivity function analysis of
       periodic circuits," 2008 IEEE/ACM International Conference on Computer-Aided Design,
       San Jose, CA, 2008, pp. 386-391.

    .. automethod:: __call__
    """
    def __init__(self, hmat, m, n, tper, k, out0):
        hmat = np.asarray(hmat)
        if hmat.shape != (2 * m + 1, n + 1):
            raise ValueError('hmat shape = %s not compatible with M=%d, N=%d' %
                             (hmat.shape, m, n))

        # use symmetry to fill in negative input frequency data.
        fullh = np.empty((2 * m + 1, 2 * n + 1), dtype=complex)
        fullh[:, n:] = hmat / (k * tper)
        fullh[:, :n] = np.fliplr(np.flipud(fullh[:, n + 1:])).conj()

        self.hmat = fullh
        wc = 2.0 * np.pi / tper
        self.m_col = np.arange(-m, m + 1) * (1.0j * wc)
        self.n_col = np.arange(-n, n + 1) * (1.0j * wc / k)
        self.m_col = self.m_col.reshape((-1, 1))
        self.n_col = self.n_col.reshape((-1, 1))
        self.tper = tper
        self.k = k
        self.outfun = interp.interp1d(out0[:, 0], out0[:, 1], bounds_error=True,
                                      assume_sorted=True)

    @staticmethod
    def _print_debug_msg(result):
        res_imag = np.imag(result).flatten()
        res_real = np.real(result).flatten()
        res_ratio = np.abs(res_imag / (res_real + 1e-18))
        idx = np.argmax(res_ratio)
        print('max imag/real ratio: %.4g, imag = %.4g, real = %.4g' %
              (res_ratio[idx], res_imag[idx], res_real[idx]))

    def __call__(self, t, tau, debug=False):
        """Calculate h(t, tau).

        Compute h(t, tau), which is the output at t subject to an impulse
        at time tau. standard numpy broadcasting rules apply.

        Parameters
        ----------
        t : array-like
            the output time.
        tau : array-like
            the input impulse time.
        debug : bool
            True to print debug messages.

        Returns
        -------
        val : :class:`numpy.ndarray`
            the time-varying impulse response evaluated at the given coordinates.
        """
        # broadcast arguments to same shape
        t, tau = np.broadcast_arrays(t, tau)

        # compute impulse using efficient matrix multiply and numpy broadcasting.
        dt = t - tau
        zero_indices = (dt < 0) | (dt > self.k * self.tper)
        t_row = t.reshape((1, -1))
        dt_row = dt.reshape((1, -1))
        tmp = np.dot(self.hmat, np.exp(np.dot(self.n_col, dt_row))) * np.exp(np.dot(self.m_col, t_row))
        result = np.sum(tmp, axis=0).reshape(dt.shape)

        # zero element such that dt < 0 or dt > k * T.
        result[zero_indices] = 0.0

        if debug:
            self._print_debug_msg(result)

        # discard imaginary part
        return np.real(result)

    def _get_core(self, num_points, debug=False):
        """Returns h(dt, tau) matrix and output waveform over 1 period.  Used by lsim.

        Compute h(dt, tau) for 0 <= tau < T and 0 <= dt < kT, where dt = t - tau.
        """
        dt_vec = np.linspace(0.0, self.k * self.tper, self.k * num_points, endpoint=False)  # type: np.ndarray
        tvec_per = dt_vec[:num_points]
        tau_col = tvec_per.reshape((-1, 1))
        dt_row = dt_vec.reshape((1, -1))
        # use matrix multiply to sum across n
        tmp = np.dot(self.hmat, np.exp(np.dot(self.n_col, dt_row)))
        # use broadcast multiply for exp(-jwm*(t-tau)) term
        tmp = tmp * np.exp(np.dot(self.m_col, dt_row))
        # use matrix multiply to sum across m
        result = np.dot(np.exp(np.dot(tau_col, self.m_col.T)), tmp).T

        if debug:
            self._print_debug_msg(result)

        # discard imaginary part
        result = np.real(result)
        # compute output waveform
        wvfm = self.outfun(tvec_per)
        return result, wvfm

    def visualize(self, fig_idx, num_points, num_period,
                  plot_color=True, plot_3d=False, show=True):
        """Visualize the time-varying impulse response.

        Parameters
        ----------
        fig_idx : int
            starting figure index.
        num_points : int
            number of sample points in a period.
        num_period : int
            number of output period.
        plot_color : bool
            True to create a plot of the time-varying impulse response as 2D color plot.
        plot_3d : bool
            True to create a 3D plot of the impulse response.
        show : bool
            True to show the plots immediately.  Set to False if you want to create some
            other plots.
        """
        if not plot_color and not plot_3d:
            # do nothing.
            return
        tot_points = num_period * num_points
        tau_vec = np.linspace(0, self.tper, num_points, endpoint=False)
        dt_vec = np.linspace(0, num_period * self.tper, tot_points, endpoint=False)
        dt, tau = np.meshgrid(dt_vec, tau_vec, indexing='ij', copy=False)
        t = tau + dt

        result, _ = self._get_core(num_points)
        result = result[:num_period * num_points, :]

        import matplotlib.pyplot as plt
        from matplotlib import cm

        if plot_color:
            # plot 2D color
            fig = plt.figure(fig_idx)
            fig_idx += 1
            ax = fig.gca()
            cp = ax.pcolor(t, tau, result, cmap=cm.cubehelix)
            plt.colorbar(cp)
            ax.set_title('Impulse response contours')
            ax.set_ylabel('impulse time')
            ax.set_xlabel('output time')

        if plot_3d:
            # plot 3D impulse response
            # noinspection PyUnresolvedReferences
            from mpl_toolkits.mplot3d import Axes3D

            fig = plt.figure(fig_idx)
            ax = fig.add_subplot(111, projection='3d')
            ax.plot_surface(t, tau, result, rstride=1, cstride=1, linewidth=0, cmap=cm.cubehelix)
            ax.set_title('Impulse response')
            ax.set_ylabel('impulse time')
            ax.set_xlabel('output time')

        if show:
            plt.show()

    def lsim(self, u, tstep, tstart=0.0, ac_only=False, periodic=False, debug=False):
        r"""Compute the output waveform given input waveform.

        This method assumes zero initial state.  The output waveform will be the
        same length as the input waveform, so pad zeros if necessary.

        Parameters
        ----------
        u : array-like
            the input waveform.
        tstep : float
            the input/output time step, in seconds.  Must evenly divide system period.
        tstart : float
            the time corresponding to u[0].  Assume u = 0 for all time before tstart.
            Defaults to 0.
        ac_only : bool
            Return output waveform due to AC input only and without steady-state
            transient.
        periodic : bool
            True if the input is periodic.  If so, returns steady state output.
        debug : bool
            True to print debug messages.

        Returns
        -------
        y : :class:`numpy.ndarray`
            the output waveform.

        Notes
        -----
        This method computes the integral:

        .. math:: y(t) = \int_{-\infty}^{\infty} h(t, \tau) \cdot x(\tau)\ d\tau

        using the following algorithm:

        #. set :math:`d\tau = \texttt{tstep}`.
        #. Compute :math:`h(\tau + dt, \tau)` for :math:`0 \le dt < kT` and
           :math:`0 \le \tau < T`, then express as a kN-by-N matrix.  This matrix
           completely describes the time-varying impulse response.
        #. tile the impulse response matrix horizontally until its number of columns
           matches input signal length, then multiply column i by u[i].
        #. Compute y as the sum of all anti-diagonals of the matrix computed in
           previous step, multiplied by :math:`d\tau`.  Truncate if necessary.
        """
        u = np.asarray(u)
        nstep = _even_quotient(self.tper, tstep)
        ndelay = _even_quotient(tstart, tstep)

        # error checking
        if len(u.shape) != 1:
            raise ValueError('u must be a 1D array.')
        if nstep < 0:
            raise ValueError('Time step = %.4g does not evenly divide'
                             'System period = %.4g' % (tstep, self.tper))
        if ndelay < 0:
            raise ValueError('Time step = %.4g does not evenly divide'
                             'Startimg time = %.4g' % (tstep, tstart))
        if periodic and nstep != u.size:
            raise ValueError('Periodic waveform must have same period as system period.')

        # calculate and tile hcore
        ntot = u.size
        hcore, outwv = self._get_core(nstep, debug=debug)
        hcore = np.roll(hcore, -ndelay, axis=1)
        outwv = np.roll(outwv, -ndelay)

        if periodic:
            # input periodic; more efficient math.
            hcore *= u
            hcore = np.tile(hcore, (1, self.k + 1))
            y = np.bincount(np.sum(np.indices(hcore.shape), axis=0).flat, hcore.flat)
            y = y[self.k * nstep:(self.k + 1) * nstep] * tstep
        else:
            ntile = int(np.ceil(ntot * 1.0 / nstep))
            hcore = np.tile(hcore, (1, ntile))
            outwv = np.tile(outwv, (ntile,))
            hcore = hcore[:, :ntot]
            outwv = outwv[:ntot]

            # broadcast multiply
            hcore *= u
            # magic code from stackoverflow
            # returns an array of the sums of all anti-diagonals.
            y = np.bincount(np.sum(np.indices(hcore.shape), axis=0).flat, hcore.flat)[:ntot] * tstep

        if not ac_only:
            # add output steady state transient
            y += outwv
        return y

    def lsim_digital(self, tsym, tstep, data, pulse, tstart=0.0, nchain=1, tdelta=0.0, **kwargs):
        """Compute output waveform given input pulse shape and data.

        This method is similar to :func:`~bag.data.ltv.LTVImpulseFinite.lsim`, but
        assumes the input is superposition of shifted and scaled copies of a given
        pulse waveform.  This assumption speeds up the computation and is useful
        for high speed link design.

        Parameters
        ----------
        tsym : float
            the symbol period, in seconds.  Must evenly divide system period.
        tstep : float
            the output time step, in seconds.  Must evenly divide symbol period.
        data : list[float]
            list of symbol values.
        pulse : np.ndarray
            the pulse waveform as a two-column array.  The first column is time,
            second column is pulse waveform value.  Linear interpolation will be used
            if necessary.  Time must start at 0.0 and be increasing.
        tstart : float
            time of the first data symbol.  Defaults to 0.0
        nchain : int
            number of blocks in a chain.  Defaults to 1.  This argument is useful if
            you have multiple blocks cascaded together in a chain, and you wish to find
            the output waveform at the end of the chain.
        tdelta : float
            time difference between adjacent elements in a chain.  Defaults to 0.  This
            argument is useful for simulating a chain of latches, where blocks operate
            on alternate phases of the clock.
        kwargs : dict[str, any]
            additional keyword arguments for :func:`~bag.data.ltv.LTVImpulseFinite.lsim`.

        Returns
        -------
        output : :class:`numpy.ndarray`
            the output waveform over N symbol period, where N is the given data length.
        """
        # check tsym evenly divides system period
        nsym = _even_quotient(self.tper, tsym)
        if nsym < 0:
            raise ValueError('Symbol period %.4g does not evenly divide '
                             'system period %.4g' % (tsym, self.tper))

        # check tstep evenly divides tsym
        nstep = _even_quotient(tsym, tstep)
        if nstep < 0:
            raise ValueError('Time step %.4g does not evenly divide '
                             'symbol period %.4g' % (tstep, tsym))

        # check tstep evenly divides tstart
        ndelay = _even_quotient(tstart, tstep)
        if ndelay < 0:
            raise ValueError('Time step %.4g does not evenly divide '
                             'starting time %.4g' % (tstep, tstart))

        nper = nstep * nsym

        pulse = np.asarray(pulse)
        tvec = pulse[:, 0]
        pvec = pulse[:, 1]

        # find input length
        # noinspection PyUnresolvedReferences
        nlast = min(np.nonzero(pvec)[0][-1] + 1, tvec.size - 1)
        tlast = tvec[nlast]
        ntot = int(np.ceil(tlast / tstep)) + nchain * self.k * nper + nstep * (nsym - 1)

        # interpolate input
        pfun = interp.interp1d(tvec, pvec, kind='linear', copy=False, bounds_error=False,
                               fill_value=0.0, assume_sorted=True)
        tin = np.linspace(0.0, ntot * tstep, ntot, endpoint=False)
        pin = pfun(tin)

        # super-impose pulse responses
        num_out = len(data) * nstep
        output = np.zeros(num_out)
        for idx in range(nsym):
            # get output pulse response
            pout = pin
            for j in range(nchain):
                pout = self.lsim(pout, tstep, tstart=tstart + j * tdelta, periodic=False,
                                 ac_only=True, **kwargs)

            # construct superposition matrix
            cur_data = data[idx::nsym]
            offsets = np.arange(0, len(cur_data) * nper, nper) * -1
            diags = np.tile(cur_data, (ntot, 1)).T
            dia_mat = sparse.dia_matrix((diags, offsets), shape=(num_out, ntot))

            # superimpose
            output += dia_mat.dot(pout)
            # shift input pulse.
            pin = np.roll(pin, nstep)

        # compute output steady state waveform
        out_pss = self.outfun(np.linspace(0.0, self.tper, nper, endpoint=False))
        out_pss = np.roll(out_pss, -ndelay)
        for j in range(1, nchain):
            out_pss = self.lsim(out_pss, tstep, tstart=tstart + j * tdelta, periodic=True,
                                ac_only=False, **kwargs)

        ntile = int(np.ceil(num_out * 1.0 / nper))
        out_pss = np.tile(out_pss, (ntile,))
        output += out_pss[:num_out]

        return output


================================================
FILE: bag/data/mos.py
================================================
# -*- coding: utf-8 -*-

"""This module defines classes for computing DC operating point.
"""

from typing import Dict

import numpy as np


def mos_y_to_ss(sim_data, char_freq, fg, ibias, cfit_method='average'):
    # type: (Dict[str, np.ndarray], float, int, np.ndarray, str) -> Dict[str, np.ndarray]
    """Convert transistor Y parameters to small-signal parameters.

    This function computes MOSFET small signal parameters from 3-port
    Y parameter measurements done on gate, drain and source, with body
    bias fixed.  This functions fits the Y parameter to a capcitor-only
    small signal model using least-mean-square error.

    Parameters
    ----------
    sim_data : Dict[str, np.ndarray]
        A dictionary of Y parameters values stored as complex numpy arrays.
    char_freq : float
        the frequency Y parameters are measured at.
    fg : int
        number of transistor fingers used for the Y parameter measurement.
    ibias : np.ndarray
        the DC bias current of the transistor.  Always positive.
    cfit_method : str
        method used to extract capacitance from Y parameters.  Currently
        supports 'average' or 'worst'

    Returns
    -------
    ss_dict : Dict[str, np.ndarray]
        A dictionary of small signal parameter values stored as numpy
        arrays.  These values are normalized to 1-finger transistor.
    """
    w = 2 * np.pi * char_freq

    gm = (sim_data['y21'].real - sim_data['y31'].real) / 2.0  # type: np.ndarray
    gds = (sim_data['y22'].real - sim_data['y32'].real) / 2.0  # type: np.ndarray
    gb = (sim_data['y33'].real - sim_data['y23'].real) / 2.0 - gm - gds  # type: np.ndarray

    cgd12 = -sim_data['y12'].imag / w
    cgd21 = -sim_data['y21'].imag / w
    cgs13 = -sim_data['y13'].imag / w
    cgs31 = -sim_data['y31'].imag / w
    cds23 = -sim_data['y23'].imag / w
    cds32 = -sim_data['y32'].imag / w
    cgg = sim_data['y11'].imag / w
    cdd = sim_data['y22'].imag / w
    css = sim_data['y33'].imag / w

    if cfit_method == 'average':
        cgd = (cgd12 + cgd21) / 2  # type: np.ndarray
        cgs = (cgs13 + cgs31) / 2  # type: np.ndarray
        cds = (cds23 + cds32) / 2  # type: np.ndarray
    elif cfit_method == 'worst':
        cgd = np.maximum(cgd12, cgd21)
        cgs = np.maximum(cgs13, cgs31)
        cds = np.maximum(cds23, cds32)
    else:
        raise ValueError('Unknown cfit_method = %s' % cfit_method)

    cgb = cgg - cgd - cgs  # type: np.ndarray
    cdb = cdd - cds - cgd  # type: np.ndarray
    csb = css - cgs - cds  # type: np.ndarray

    ibias = ibias / fg
    gm = gm / fg
    gds = gds / fg
    gb = gb / fg
    cgd = cgd / fg
    cgs = cgs / fg
    cds = cds / fg
    cgb = cgb / fg
    cdb = cdb / fg
    csb = csb / fg

    return dict(
        ibias=ibias,
        gm=gm,
        gds=gds,
        gb=gb,
        cgd=cgd,
        cgs=cgs,
        cds=cds,
        cgb=cgb,
        cdb=cdb,
        csb=csb,
    )


================================================
FILE: bag/data/plot.py
================================================
# -*- coding: utf-8 -*-

"""This module contains utilities to improve waveform plotting in python.
"""

import numpy as np
import scipy.interpolate as interp

from matplotlib.lines import Line2D
from matplotlib.figure import Figure
from matplotlib.text import Annotation
import matplotlib.pyplot as plt

from ..math import float_to_si_string

# Vega category10 palette
color_cycle = ['#1f77b4', '#ff7f0e',
               '#2ca02c', '#d62728',
               '#9467bd', '#8c564b',
               '#e377c2', '#7f7f7f',
               '#bcbd22', '#17becf',
               ]


def figure(fig_id, picker=5.0):
    """Create a WaveformPlotter.

    Parameters
    ----------
    fig_id : int
        the figure ID.
    picker : float
        picker event pixel tolerance.

    Returns
    -------
    plotter : bag.data.plot.WaveformPlotter
        a plotter that helps you make interactive matplotlib figures.
    """
    return WaveformPlotter(fig_id, picker=picker)


def plot_waveforms(xvec, panel_list, fig=1):
    """Plot waveforms in vertical panels with shared X axis.

    Parameters
    ----------
    xvec : :class:`numpy.ndarray`
        the X data.
    panel_list : list[list[(str, :class:`numpy.ndarray`)]]
        list of lists of Y data.  Each sub-list is one panel.  Each element of the sub-list
        is a tuple of signal name and signal data.
    fig : int
        the figure ID.
    """
    nrow = len(panel_list)

    if nrow > 0:
        myfig = plt.figure(fig, FigureClass=MarkerFigure)  # type: MarkerFigure
        ax0 = None
        for idx, panel in enumerate(panel_list):
            if ax0 is None:
                ax = plt.subplot(nrow, 1, idx + 1)
                ax0 = ax
            else:
                ax = plt.subplot(nrow, 1, idx + 1, sharex=ax0)

            for name, sig in panel:
                ax.plot(xvec, sig, label=name, picker=5.0)

            box = ax.get_position()
            ax.set_position([box.x0, box.y0, box.width * 0.9, box.height])

            # Put a legend to the right of the current axis
            ax.legend(loc='center left', bbox_to_anchor=(1, 0.5))

        myfig.setup_callbacks()
        plt.show(block=False)


def _fpart(x):
    return x - int(x)


def _rfpart(x):
    return 1 - _fpart(x)


def draw_line(x0, y0, x1, y1, xmax, grid):
    """Draws an anti-aliased line in img from p1 to p2 with the given color."""

    if x0 > x1:
        # x1 is wrapped around
        x1 += xmax

    dx, dy = x1 - x0, y1 - y0
    steep = dx < abs(dy)
    if steep:
        x0, y0, x1, y1, dx, dy = y0, x0, y1, x1, dy, dx

    gradient = dy * 1.0 / dx
    # handle first endpoint
    xpxl1 = int(x0 + 0.5)
    yend = y0 + gradient * (xpxl1 - x0)
    xgap = _rfpart(x0 + 0.5)
    ypxl1 = int(yend)
    if steep:
        grid[ypxl1 % xmax, xpxl1] += _rfpart(yend) * xgap
        grid[(ypxl1 + 1) % xmax, xpxl1] += _fpart(yend) * xgap
    else:
        grid[xpxl1 % xmax, ypxl1] += _rfpart(yend) * xgap
        grid[xpxl1 % xmax, ypxl1 + 1] += _fpart(yend) * xgap

    intery = yend + gradient  # first y-intersection for the main loop

    # do not color second endpoint to avoid double coloring.
    xpxl2 = int(x1 + 0.5)
    # main loop
    if steep:
        for x in range(xpxl1 + 1, xpxl2):
            xval = int(intery)
            grid[xval % xmax, x] += _rfpart(intery)
            grid[(xval + 1) % xmax, x] += _fpart(intery)
            intery += gradient
    else:
        for x in range(xpxl1 + 1,  xpxl2):
            xval = x % xmax
            grid[xval, int(intery)] += _rfpart(intery)
            grid[xval, int(intery) + 1] += _fpart(intery)
            intery += gradient


def plot_eye_heatmap(fig, tvec, yvec, tper, tstart=None, tend=None, toff=None,
                     tstep=None, vstep=None,
                     cmap=None, vmargin=0.05, interpolation='gaussian',
                     repeat=False):
    """Plot eye diagram heat map.

    Parameters
    ----------
    fig : int
        the figure ID.
    tvec : np.ndarray
        the time data.
    yvec : np.ndarray
        waveform data.
    tper : float
        the eye period.
    tstart : float
        starting time.  Defaults to first point.
    tend : float
        ending time.  Defaults to last point.
    toff : float
        eye offset.  Defaults to 0.
    tstep : float or None
        horizontal bin size.  Defaults to using 200 bins.
    vstep : float or None
        vertical bin size.  Defaults to using 200 bins.
    cmap :
        the colormap used for coloring the heat map.  If None, defaults to cubehelix_r
    vmargin : float
        vertical margin in percentage of maximum/minimum waveform values.  Defaults
        to 5 percent.  This is used so that there some room between top/bottom of
        eye and the plot.
    interpolation : str
        interpolation method.  Defaults to 'gaussian'.  Use 'none' for no interpolation.
    repeat : bool
        True to repeat the eye diagram once to the right.  This is useful if you
        want to look at edge transistions.
    """
    if not toff:
        toff = 0.0
    if tstart is None:
        tstart = tvec[0]
    if tend is None:
        tend = tvec[-1]

    if tstep is None:
        num_h = 200
    else:
        num_h = int(np.ceil(tper / tstep))

    arr_idx = (tstart <= tvec) & (tvec < tend)
    tplot = np.mod((tvec[arr_idx] - toff), tper) / tper * num_h  # type: np.ndarray
    yplot = yvec[arr_idx]

    # get vertical range
    ymin, ymax = np.amin(yplot), np.amax(yplot)
    yrang = (ymax - ymin) * (1 + vmargin)
    ymid = (ymin + ymax) / 2.0
    ymin = ymid - yrang / 2.0
    ymax = ymin + yrang

    if vstep is None:
        num_v = 200
    else:
        num_v = int(np.ceil(yrang / vstep))

    # rescale Y axis
    yplot = (yplot - ymin) / yrang * num_v

    grid = np.zeros((num_h, num_v), dtype=float)
    for idx in range(yplot.size - 1):
        draw_line(tplot[idx], yplot[idx], tplot[idx + 1], yplot[idx + 1], num_h, grid)

    if cmap is None:
        from matplotlib import cm
        # noinspection PyUnresolvedReferences
        cmap = cm.cubehelix_r

    plt.figure(fig)
    grid = grid.T[::-1, :]
    if repeat:
        grid = np.tile(grid, (1, 2))
        tper *= 2.0
    plt.imshow(grid, extent=[0, tper, ymin, ymax], cmap=cmap,
               interpolation=interpolation, aspect='auto')
    cb = plt.colorbar()
    cb.set_label('counts')
    return grid


def plot_eye(fig, tvec, yvec_list, tper, tstart=None, tend=None,
             toff_list=None, name_list=None, alpha=1.0):
    """Plot eye diagram.

    Parameters
    ----------
    fig : int
        the figure ID.
    tvec : np.ndarray
        the time data.
    yvec_list : list[np.ndarray]
        list of waveforms to plot in eye diagram.
    tper : float
        the period.
    tstart : float
        starting time.  Defaults to first point.
    tend : float
        ending time.  Defaults to last point.
    toff_list : list[float]
        offset to apply to each waveform.  Defaults to zeros.
    name_list : list[str] or None
        the name of each waveform.  Defaults to numbers.
    alpha : float
        the transparency of each trace.  Can be used to mimic heatmap.
    """
    if not yvec_list:
        return

    if not name_list:
        name_list = [str(num) for num in range(len(yvec_list))]
    if not toff_list:
        toff_list = [0.0] * len(yvec_list)
    if tstart is None:
        tstart = tvec[0]
    if tend is None:
        tend = tvec[-1]

    # get new tstep that evenly divides tper and new x vector
    tstep_given = (tvec[-1] - tvec[0]) / (tvec.size - 1)
    num_samp = int(round(tper / tstep_given))
    t_plot = np.linspace(0.0, tper, num_samp, endpoint=False)

    # find tstart and tend in number of tper.
    nstart = int(np.floor(tstart / tper))
    nend = int(np.ceil(tend / tper))
    ncycle = nend - nstart
    teye = np.linspace(nstart * tper, nend * tper, num_samp * ncycle, endpoint=False)  # type: np.ndarray
    teye = teye.reshape((ncycle, num_samp))

    myfig = plt.figure(fig, FigureClass=MarkerFigure)  # type: MarkerFigure
    ax = plt.subplot()
    legend_lines = []
    for idx, yvec in enumerate(yvec_list):
        color = color_cycle[idx % len(color_cycle)]
        toff = toff_list[idx]
        # get eye traces
        yfun = interp.interp1d(tvec - toff, yvec, kind='linear', copy=False, bounds_error=False,
                               fill_value=np.nan, assume_sorted=True)
        plot_list = []
        for cycle_idx in range(ncycle):
            plot_list.append(t_plot)
            plot_list.append(yfun(teye[cycle_idx, :]))

        lines = ax.plot(*plot_list, alpha=alpha, color=color, picker=4.0, linewidth=2)
        legend_lines.append(lines[0])

    # Put a legend to the right of the current axis
    box = ax.get_position()
    ax.set_position([box.x0, box.y0, box.width * 0.9, box.height])
    ax.legend(legend_lines, name_list, loc='center left', bbox_to_anchor=(1, 0.5))

    myfig.setup_callbacks()
    plt.show(block=False)


def _find_closest_point(x, y, xvec, yvec, xnorm, ynorm):
    """Find point on PWL waveform described by xvec, yvec closest to (x, y)"""
    xnvec = xvec / xnorm
    ynvec = yvec / ynorm
    xn = x / xnorm
    yn = y / ynorm

    dx = np.diff(xnvec)
    dy = np.diff(ynvec)
    px = (xn - xnvec[:-1])
    py = (yn - ynvec[:-1])

    that = (px * dx + py * dy) / (dx ** 2 + dy ** 2)
    t = np.minimum(np.maximum(that, 0), 1)

    minx = xnvec[:-1] + t * dx
    miny = ynvec[:-1] + t * dy

    dist = (minx - xn) ** 2 + (miny - yn) ** 2
    idx = np.argmin(dist)
    return minx[idx] * xnorm, miny[idx] * ynorm


class WaveformPlotter(object):
    """A custom matplotlib interactive plotting class.

    This class adds many useful features, such as ability to add/remove markers,
    ability to toggle waveforms on and off, and so on.

    Parameters
    ----------
    fig_idx : int
        the figure index.
    picker : float
        picker event pixel tolerance.
    normal_width : float
        normal linewidth.
    select_width : float
        selected linewidth.
    """

    def __init__(self, fig_idx, picker=5.0, normal_width=1.5, select_width=3.0):
        self.figure = plt.figure(fig_idx, FigureClass=MarkerFigure)  # type: MarkerFigure
        self.picker = picker
        self.norm_lw = normal_width
        self.top_lw = select_width
        self.ax = self.figure.gca()
        self.ax.set_prop_cycle('color', color_cycle)
        self.leline_lookup = {}
        self.letext_lookup = {}
        self.last_top = None
        self.legend = None
        self.resized_legend = False

    def plot(self, *args, **kwargs):
        if self.figure is None:
            raise ValueError('figure closed already')

        if 'picker' not in kwargs:
            kwargs['picker'] = self.picker
        kwargs['linewidth'] = self.norm_lw
        if 'lw' in kwargs:
            del kwargs['lw']
        return self.ax.plot(*args, **kwargs)

    def setup(self):
        if self.figure is None:
            raise ValueError('figure closed already')

        self.figure.tight_layout()
        # Put a legend to the right of the current axis
        ax_lines, ax_labels = self.ax.get_legend_handles_labels()
        self.legend = self.ax.legend(ax_lines, ax_labels, loc='center left',
                                     bbox_to_anchor=(1, 0.5), fancybox=True)
        le_lines = self.legend.get_lines()
        le_texts = self.legend.get_texts()

        for leline, letext, axline in zip(le_lines, le_texts, ax_lines):
            self.leline_lookup[leline] = (letext, axline)
            self.letext_lookup[letext] = (leline, axline)
            leline.set_picker(self.picker)
            letext.set_picker(self.picker)
            letext.set_alpha(0.5)

        le_texts[-1].set_alpha(1.0)
        ax_lines[-1].set_zorder(2)
        ax_lines[-1].set_linewidth(self.top_lw)
        self.last_top = (le_texts[-1], ax_lines[-1])

        self.figure.register_pick_event(self.leline_lookup, self.legend_line_picked)
        self.figure.register_pick_event(self.letext_lookup, self.legend_text_picked)
        self.figure.setup_callbacks()
        self.figure.canvas.mpl_connect('draw_event', self.fix_legend_location)
        self.figure.canvas.mpl_connect('close_event', self.figure_closed)
        self.figure.canvas.mpl_connect('resize_event', self.figure_resized)

    # noinspection PyUnusedLocal
    def figure_closed(self, event):
        self.figure.close_figure()
        self.figure = None
        self.ax = None
        self.leline_lookup = None
        self.letext_lookup = None
        self.last_top = None
        self.legend = None

    # noinspection PyUnusedLocal
    def figure_resized(self, event):
        self.resized_legend = False
        self.fix_legend_location(None)

    # noinspection PyUnusedLocal
    def fix_legend_location(self, event):
        if not self.resized_legend:
            self.figure.tight_layout()
            inv_tran = self.figure.transFigure.inverted()
            leg_box = inv_tran.transform(self.legend.get_window_extent())
            leg_width = leg_box[1][0] - leg_box[0][0]
            box = self.ax.get_position()
            # print box.x0, box.y0, box.width, box.height, leg_width, leg_frame.get_height()
            self.ax.set_position([box.x0, box.y0, box.width - leg_width, box.height])
            self.resized_legend = True
            self.figure.canvas.draw()

    def legend_line_picked(self, artist):
        letext, axline = self.leline_lookup[artist]
        visible = not axline.get_visible()
        if visible:
            artist.set_alpha(1.0)
        else:
            artist.set_alpha(0.2)
        if visible and (self.last_top[1] is not axline):
            # set to be top line
            self.legend_text_picked(letext, draw=False)
        self.figure.set_line_visibility(axline, visible)

    def legend_text_picked(self, artist, draw=True):
        leline, axline = self.letext_lookup[artist]
        self.last_top[0].set_alpha(0.5)
        self.last_top[1].set_zorder(1)
        self.last_top[1].set_linewidth(self.norm_lw)
        axline.set_zorder(2)
        artist.set_alpha(1.0)
        axline.set_linewidth(self.top_lw)
        self.last_top = (artist, axline)

        # if draw is False, this method is not called from
        # legend_line_picked(), so we'll never have recursion issues.
        if draw:
            if not axline.get_visible():
                # set line to be visible if not
                # draw() will be called in legend_line_picked
                self.legend_line_picked(leline)
            else:
                self.figure.canvas.draw()


# noinspection PyAbstractClass
class MarkerFigure(Figure):
    def __init__(self, **kwargs):
        Figure.__init__(self, **kwargs)
        self.markers = []
        self.epsilon = 10.0
        self.drag_idx = -1
        self.timer = None
        self.marker_line_info = None
        self.pick_sets = []
        self.pick_funs = []

    def set_line_visibility(self, axline, visible):
        axline.set_visible(visible)
        if not visible:
            # delete all markers on this line
            del_idx_list = [idx for idx, item in enumerate(self.markers) if item[2] is axline]
            for targ_idx in reversed(del_idx_list):
                an, pt, _, _ = self.markers[targ_idx]
                del self.markers[targ_idx]
                # print targ_idx, an
                an.set_visible(False)
                pt.set_visible(False)

        self.canvas.draw()

    def register_pick_event(self, artist_set, fun):
        self.pick_sets.append(artist_set)
        self.pick_funs.append(fun)

    def on_button_release(self, event):
        """Disable data cursor dragging. """
        if event.button == 1:
            self.drag_idx = -1

    def on_motion(self, event):
        """Move data cursor around. """
        ax = event.inaxes
        if self.drag_idx >= 0 and ax is not None and event.button == 1:
            xmin, xmax = ax.get_xlim()
            ymin, ymax = ax.get_ylim()
            anno, pt, line, bg = self.markers[self.drag_idx]
            x, y = _find_closest_point(event.xdata, event.ydata,
                                       line.get_xdata(), line.get_ydata(),
                                       xmax - xmin, ymax - ymin)
            pt.set_data([x], [y])
            xstr, ystr = float_to_si_string(x, 4), float_to_si_string(y, 4)
            anno.set_text('x: %s\ny: %s' % (xstr, ystr))
            anno.xy = (x, y)
            self.canvas.restore_region(bg)
            anno.set_visible(True)
            pt.set_visible(True)
            ax.draw_artist(anno)
            ax.draw_artist(pt)
            self.canvas.blit(ax.bbox)

    def _get_idx_under_point(self, event):
        """Find selected data cursor."""
        mx = event.x
        my = event.y
        mind = None
        minidx = None
        # find closest marker point
        for idx, (an, pt, _, _) in enumerate(self.markers):
            xv, yv = pt.get_xdata()[0], pt.get_ydata()[0]
            xp, yp = event.inaxes.transData.transform([xv, yv])
            # print xv, yv, xp, yp, mx, my
            d = ((mx - xp) ** 2 + (my - yp) ** 2) ** 0.5
            if mind is None or d < mind:
                mind = d
                minidx = idx

        if mind is not None and mind < self.epsilon:
            return minidx
        return -1

    def on_pick(self, event):
        artist = event.artist
        if not artist.get_visible():
            return
        for idx, artist_set in enumerate(self.pick_sets):
            if artist in artist_set:
                self.pick_funs[idx](artist)
                return

        if isinstance(artist, Line2D):
            mevent = event.mouseevent
            # figure out if we picked marker or line
            self.drag_idx = self._get_idx_under_point(mevent)

            if self.drag_idx >= 0:
                # picked marker.
                ax = mevent.inaxes
                an, pt, _, _ = self.markers[self.drag_idx]
                an.set_visible(False)
                pt.set_visible(False)
                self.canvas.draw()
                self.markers[self.drag_idx][-1] = self.canvas.copy_from_bbox(ax.bbox)
                an.set_visible(True)
                pt.set_visible(True)
                ax.draw_artist(an)
                ax.draw_artist(pt)
                self.canvas.blit(ax.bbox)

            else:
                # save data to plot marker later
                mxval = mevent.xdata
                button = mevent.button
                if mxval is not None and button == 1 and not self.marker_line_info:
                    self.marker_line_info = (artist, mxval, mevent.ydata,
                                             button, mevent.inaxes)
        elif isinstance(artist, Annotation):
            # delete marker.
            mevent = event.mouseevent
            if mevent.button == 3:
                targ_idx = None
                for idx, (an, pt, _, _) in enumerate(self.markers):
                    if an is artist:
                        targ_idx = idx
                        break
                if targ_idx is not None:
                    an, pt, _, _ = self.markers[targ_idx]
                    del self.markers[targ_idx]
                    an.set_visible(False)
                    pt.set_visible(False)
                    self.canvas.draw()

    def _create_marker(self):
        if self.marker_line_info:
            artist, mxval, myval, button, ax = self.marker_line_info
            xmin, xmax = ax.get_xlim()
            ymin, ymax = ax.get_ylim()
            mxval, myval = _find_closest_point(mxval, myval,
                                               artist.get_xdata(), artist.get_ydata(),
                                               xmax - xmin, ymax - ymin)
            pt = ax.plot(mxval, myval, 'ko', picker=5.0)[0]
            xstr, ystr = float_to_si_string(mxval, 4), float_to_si_string(myval, 4)
            msg = 'x: %s\ny: %s' % (xstr, ystr)
            anno = ax.annotate(msg, xy=(mxval, myval), bbox=dict(boxstyle='round', fc='yellow', alpha=0.3),
                               arrowprops=dict(arrowstyle="->"))
            anno.draggable()
            anno.set_picker(True)

            self.markers.append([anno, pt, artist, None])
            ax.draw_artist(anno)
            ax.draw_artist(pt)
            self.canvas.blit(ax.bbox)
            self.marker_line_info = None

    def close_figure(self):
        self.timer.stop()

    def setup_callbacks(self):
        self.canvas.mpl_connect('pick_event', self.on_pick)
        self.canvas.mpl_connect('motion_notify_event', self.on_motion)
        self.canvas.mpl_connect('button_release_event', self.on_button_release)
        # use timer to make sure we won't create multiple markers at once when
        # clicked on overlapping lines.
        self.timer = self.canvas.new_timer(interval=100)
        self.timer.add_callback(self._create_marker)
        self.timer.start()


================================================
FILE: bag/design/LICENSE
================================================
BSD 3-Clause License

Copyright (c) 2018, Regents of the University of California
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

* Redistributions of source code must retain the above copyright notice, this
  list of conditions and the following disclaimer.

* Redistributions in binary form must reproduce the above copyright notice,
  this list of conditions and the following disclaimer in the documentation
  and/or other materials provided with the distribution.

* Neither the name of the copyright holder nor the names of its
  contributors may be used to endorse or promote products derived from
  this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.


================================================
FILE: bag/design/__init__.py
================================================
# -*- coding: utf-8 -*-

"""This package defines design template classes.
"""

from .module import Module, ModuleDB, SchInstance, MosModuleBase, ResPhysicalModuleBase, ResMetalModule

__all__ = ['Module', 'ModuleDB', 'SchInstance', 'MosModuleBase', 'ResPhysicalModuleBase', 'ResMetalModule']


================================================
FILE: bag/design/module.py
================================================
# -*- coding: utf-8 -*-

"""This module defines base design module class and primitive design classes.
"""

import os
import abc
from typing import TYPE_CHECKING, List, Dict, Optional, Tuple, Any, Type, Set, Sequence, \
    Callable, Union

from ..math import float_to_si_string
from ..io import read_yaml
from ..util.cache import DesignMaster, MasterDB

if TYPE_CHECKING:
    from ..core import BagProject
    from ..layout.core import TechInfo


class ModuleDB(MasterDB):
    """A database of all modules.

    This class is responsible for keeping track of module libraries and
    creating new modules.

    Parameters
    ----------
    lib_defs : str
        path to the design library definition file.
    tech_info : TechInfo
        the TechInfo instance.
    sch_exc_libs : List[str]
        list of libraries that are excluded from import.
    prj : Optional[BagProject]
        the BagProject instance.
    name_prefix : str
        generated layout name prefix.
    name_suffix : str
        generated layout name suffix.
    lib_path : str
        path to create generated library in.
    """

    def __init__(self, lib_defs, tech_info, sch_exc_libs, prj=None, name_prefix='',
                 name_suffix='', lib_path=''):
        # type: (str, TechInfo, List[str], Optional[BagProject], str, str, str) -> None
        MasterDB.__init__(self, '', lib_defs=lib_defs, name_prefix=name_prefix,
                          name_suffix=name_suffix)

        self._prj = prj
        self._tech_info = tech_info
        self._exc_libs = set(sch_exc_libs)
        self.lib_path = lib_path

    def create_master_instance(self, gen_cls, lib_name, params, used_cell_names, **kwargs):
        # type: (Type[Module], str, Dict[str, Any], Set[str], **Any) -> Module
        """Create a new non-finalized master instance.

        This instance is used to determine if we created this instance before.

        Parameters
        ----------
        gen_cls : Type[Module]
            the generator Python class.
        lib_name : str
            generated instance library name.
        params : Dict[str, Any]
            instance parameters dictionary.
        used_cell_names : Set[str]
            a set of all used cell names.
        **kwargs : Any
            optional arguments for the generator.

        Returns
        -------
        master : Module
            the non-finalized generated instance.
        """
        kwargs = kwargs.copy()
        kwargs['lib_name'] = lib_name
        kwargs['params'] = params
        kwargs['used_names'] = used_cell_names
        # noinspection PyTypeChecker
        return gen_cls(self, **kwargs)

    def create_masters_in_db(self, lib_name, content_list, debug=False):
        # type: (str, Sequence[Any], bool) -> None
        """Create the masters in the design database.

        Parameters
        ----------
        lib_name : str
            library to create the designs in.
        content_list : Sequence[Any]
            a list of the master contents.  Must be created in this order.
        debug : bool
            True to print debug messages
        """
        if self._prj is None:
            raise ValueError('BagProject is not defined.')

        self._prj.instantiate_schematic(lib_name, content_list, lib_path=self.lib_path)

    @property
    def tech_info(self):
        # type: () -> TechInfo
        """the :class:`~bag.layout.core.TechInfo` instance."""
        return self._tech_info

    def is_lib_excluded(self, lib_name):
        # type: (str) -> bool
        """Returns true if the given schematic library does not contain generators.

        Parameters
        ----------
        lib_name : str
            library name

        Returns
        -------
        is_excluded : bool
            True if given library is excluded.
        """
        return lib_name in self._exc_libs


class SchInstance(object):
    """A class representing a schematic instance.

    Parameters
    ----------
    database : ModuleDB
        the schematic generator database.
    gen_lib_name : str
        the schematic generator library name.
    gen_cell_name : str
        the schematic generator cell name.
    inst_name : str
        name of this instance.
    static : bool
        True if the schematic generator is static.
    connections : Optional[Dict[str, str]]
        If given, initialize instance terminal connections to this dictionary.
    master : Optional[Module]
        If given, set the master of this instance.
    parameters : Optional[Dict[str, Any]]
        If given, set the instance parameters to this dictionary.
    """

    def __init__(self,
                 database,  # type: MasterDB
                 gen_lib_name,  # type: str
                 gen_cell_name,  # type: str
                 inst_name,  # type: str
                 static=False,  # type: bool
                 connections=None,  # type: Optional[Dict[str, str]]
                 master=None,  # type: Optional[Module]
                 parameters=None,  # type: Optional[Dict[str, Any]]
                 ):
        # type: (...) -> None
        self._db = database
        self._master = master
        self._name = inst_name
        self._gen_lib_name = gen_lib_name
        self._gen_cell_name = gen_cell_name
        self._static = static
        self._term_mapping = {} if connections is None else connections
        self.parameters = {} if parameters is None else parameters

    def change_generator(self, gen_lib_name, gen_cell_name, static=False):
        # type: (str, str, bool) -> None
        """Change the master associated with this instance.

        All instance parameters and terminal mappings will be reset.

        Parameters
        ----------
        gen_lib_name : str
            the new schematic generator library name.
        gen_cell_name : str
            the new schematic generator cell name.
        static : bool
            True if the schematic generator is static.
        """
        self._master = None
        self._gen_lib_name = gen_lib_name
        self._gen_cell_name = gen_cell_name
        self._static = static
        self.parameters.clear()
        self._term_mapping.clear()

    @property
    def name(self):
        # type: () -> str
        """Returns the instance name."""
        return self._name

    @property
    def connections(self):
        # type: () -> Dict[str, str]
        """Returns the instance terminals connection dictionary."""
        return self._term_mapping

    @property
    def is_primitive(self):
        # type: () -> bool
        """Returns true if this is an instance of a primitive schematic generator."""
        if self._static:
            return True
        if self._master is None:
            raise ValueError('Instance %s has no master.  '
                             'Did you forget to call design()?' % self._name)
        return self._master.is_primitive()

    @property
    def should_delete(self):
        # type: () -> bool
        """Returns true if this instance should be deleted."""
        return self._master is not None and self._master.should_delete_instance()

    @property
    def master(self):
        # type: () -> Optional[Module]
        return self._master

    @property
    def master_cell_name(self):
        # type: () -> str
        """Returns the schematic master cell name."""
        return self._gen_cell_name if self._master is None else self._master.cell_name

    @property
    def master_key(self):
        # type: () -> Any
        return self._master.key

    def copy(self, inst_name, connections=None):
        # type: (str, Optional[Dict[str, str]]) -> SchInstance
        """Returns a copy of this SchInstance.

        Parameters
        ----------
        inst_name : str
            the new instance name.
        connections : Optional[Dict[str, str]]
            If given, will set the connections of this instance to this dictionary.

        Returns
        -------
        sch_inst : SchInstance
            a copy of this SchInstance, with connections potentially updated.
        """
        if connections is None:
            connections = self._term_mapping.copy()
        return SchInstance(self._db, self._gen_lib_name, self._gen_cell_name, inst_name,
                           static=self._static, connections=connections, master=self._master,
                           parameters=self.parameters.copy())

    def get_master_lib_name(self, impl_lib):
        # type: (str) -> str
        """Returns the schematic master library name.

        Parameters
        ----------
        impl_lib : str
            library where schematic masters will be created.

        Returns
        -------
        master_lib : str
            the schematic master library name.
        """
        return self._gen_lib_name if self.is_primitive else impl_lib

    def design_specs(self, *args, **kwargs):
        # type: (*Any, **Any) -> None
        """Update the instance master."""
        self._update_master('design_specs', args, kwargs)

    def design(self, *args, **kwargs):
        # type: (*Any, **Any) -> None
        """Update the instance master."""
        self._update_master('design', args, kwargs)

    def _update_master(self, design_fun, args, kwargs):
        # type: (str, Tuple[Any, ...], Dict[str, Any]) -> None
        """Create a new master."""
        if args:
            key = 'args'
            idx = 1
            while key in kwargs:
                key = 'args_%d' % idx
                idx += 1
            kwargs[key] = args
        else:
            key = None
        self._master = self._db.new_master(self._gen_lib_name, self._gen_cell_name,
                                           params=kwargs, design_args=key,
                                           design_fun=design_fun)  # type: Module
        if self._master.is_primitive():
            self.parameters.update(self._master.get_schematic_parameters())

    def implement_design(self, lib_name, top_cell_name='', prefix='', suffix='', **kwargs):
        # type: (str, str, str, str, **Any) -> None
        """Implement this design module in the given library.

        If the given library already exists, this method will not delete or override
        any pre-existing cells in that library.

        If you use this method, you do not need to call update_structure(),
        as this method calls it for you.

        This method only works if BagProject is given.

        Parameters
        ----------
        lib_name : str
            name of the new library to put the generated schematics.
        top_cell_name : str
            the cell name of the top level design.
        prefix : str
            prefix to add to cell names.
        suffix : str
            suffix to add to cell names.
        **kwargs : Any
            additional arguments.
        """
        if 'erase' in kwargs:
            print('DEPRECATED WARNING: erase is no longer supported '
                  'in implement_design() and has no effect')

        debug = kwargs.get('debug', False)
        rename_dict = kwargs.get('rename_dict', None)

        if not top_cell_name:
            top_cell_name = None

        if 'lib_path' in kwargs:
            self._db.lib_path = kwargs['lib_path']
        self._db.cell_prefix = prefix
        self._db.cell_suffix = suffix
        self._db.instantiate_masters([self._master], [top_cell_name], lib_name=lib_name,
                                     debug=debug, rename_dict=rename_dict)

    def get_layout_params(self, **kwargs):
        # type: (Any) -> Dict[str, Any]
        """Backwards compatibility function."""
        if hasattr(self._master, 'get_layout_params'):
            return getattr(self._master, 'get_layout_params')(**kwargs)
        else:
            return kwargs


class Module(DesignMaster, metaclass=abc.ABCMeta):
    """The base class of all schematic generators.  This represents a schematic master.

    This class defines all the methods needed to implement a design in the CAD database.

    Parameters
    ----------
    database : ModuleDB
        the design database object.
    yaml_fname : str
        the netlist information file name.
    **kwargs :
        additional arguments

    Attributes
    ----------
    parameters : dict[str, any]
        the design parameters dictionary.
    instances : dict[str, None or :class:`~bag.design.Module` or list[:class:`~bag.design.Module`]]
        the instance dictionary.
    """

    # noinspection PyUnusedLocal
    def __init__(self, database, yaml_fname, **kwargs):
        # type: (ModuleDB, str, **Any) -> None

        lib_name = kwargs['lib_name']
        params = kwargs['params']
        used_names = kwargs['used_names']
        design_fun = kwargs['design_fun']
        design_args = kwargs['design_args']

        self.tech_info = database.tech_info
        self.instances = {}  # type: Dict[str, Union[SchInstance, List[SchInstance]]]
        self.pin_map = {}
        self.new_pins = []
        self.parameters = {}
        self._pin_list = None

        self._yaml_fname = os.path.abspath(yaml_fname)
        self.sch_info = read_yaml(self._yaml_fname)

        self._orig_lib_name = self.sch_info['lib_name']
        self._orig_cell_name = self.sch_info['cell_name']
        self._design_fun = design_fun
        self._design_args = design_args

        # create initial instances and populate instance map
        for inst_name, inst_attr in self.sch_info['instances'].items():
            lib_name = inst_attr['lib_name']
            cell_name = inst_attr['cell_name']
            static = database.is_lib_excluded(lib_name)
            self.instances[inst_name] = SchInstance(database, lib_name, cell_name, inst_name,
                                                    static=static)

        # fill in pin map
        for pin in self.sch_info['pins']:
            self.pin_map[pin] = pin

        # initialize schematic master
        DesignMaster.__init__(self, database, lib_name, params, used_names)

    @property
    def pin_list(self):
        # type: () -> List[str]
        return self._pin_list

    @abc.abstractmethod
    def design(self, **kwargs):
        """To be overridden by subclasses to design this module.

        To design instances of this module, you can
        call their :meth:`.design` method or any other ways you coded.

        To modify schematic structure, call:

        :meth:`.rename_pin`

        :meth:`.delete_instance`

        :meth:`.replace_instance_master`

        :meth:`.reconnect_instance_terminal`

        :meth:`.array_instance`
        """
        pass

    def finalize(self):
        # type: () -> None
        """Finalize this master instance.
        """
        # invoke design function
        fun = getattr(self, self._design_fun)
        if self._design_args:
            args = self.params.pop(self._design_args)
            fun(*args, **self.params)
        else:
            fun(**self.params)

        # backwards compatibility
        if self.key is None:
            self.params.clear()
            self.params.update(self.parameters)
            self.update_master_info()

        self.children = set()
        for inst_list in self.instances.values():

            if isinstance(inst_list, SchInstance):
                if not inst_list.is_primitive:
                    self.children.add(inst_list.master_key)
            else:
                for inst in inst_list:
                    if not inst.is_primitive:
                        self.children.add(inst.master_key)

        # compute pins
        self._pin_list = [pin_name for pin_name, _ in self.new_pins]
        self._pin_list.extend((val for val in self.pin_map.values() if val))

        # call super finalize routine
        super(Module, self).finalize()

    @classmethod
    def get_params_info(cls):
        # type: () -> Optional[Dict[str, str]]
        """Returns a dictionary from parameter names to descriptions.

        Returns
        -------
        param_info : Optional[Dict[str, str]]
            dictionary from parameter names to descriptions.
        """
        return None

    def get_master_basename(self):
        # type: () -> str
        """Returns the base name to use for this instance.

        Returns
        -------
        basename : str
            the base name for this instance.
        """
        return self._orig_cell_name

    def get_content(self, lib_name, rename_fun):
        # type: (str, Callable[[str], str]) -> Optional[Tuple[Any,...]]
        """Returns the content of this master instance.

        Parameters
        ----------
        lib_name : str
            the library to create the design masters in.
        rename_fun : Callable[[str], str]
            a function that renames design masters.

        Returns
        -------
        content : Optional[Tuple[Any,...]]
            the master content data structure.
        """
        if self.is_primitive():
            return None

        # populate instance transform mapping dictionary
        inst_map = {}
        for inst_name, inst_list in self.instances.items():
            if isinstance(inst_list, SchInstance):
                inst_list = [inst_list]

            info_list = []
            for inst in inst_list:
                if not inst.should_delete:
                    cur_lib = inst.get_master_lib_name(lib_name)
                    info_list.append(dict(
                        name=inst.name,
                        lib_name=cur_lib,
                        cell_name= inst.master_cell_name if inst.is_primitive else rename_fun(inst.master_cell_name),
                        params=inst.parameters,
                        term_mapping=inst.connections,
                    ))
            inst_map[inst_name] = info_list

        return (self._orig_lib_name, self._orig_cell_name, rename_fun(self.cell_name),
                self.pin_map, inst_map, self.new_pins)

    @property
    def cell_name(self):
        # type: () -> str
        """The master cell name."""
        if self.is_primitive():
            return self.get_cell_name_from_parameters()
        return super(Module, self).cell_name

    @property
    def orig_cell_name(self):
        # type: () -> str
        """The original schematic template cell name."""
        return self._orig_cell_name

    def is_primitive(self):
        # type: () -> bool
        """Returns True if this Module represents a BAG primitive.

        NOTE: This method is only used by BAG and schematic primitives.  This method prevents
        the module from being copied during design implementation.  Custom subclasses should
        not override this method.

        Returns
        -------
        is_primitive : bool
            True if this Module represents a BAG primitive.
        """
        return False

    def should_delete_instance(self):
        # type: () -> bool
        """Returns True if this instance should be deleted based on its parameters.

        This method is mainly used to delete 0 finger or 0 width transistors.  However,
        You can override this method if there exists parameter settings which corresponds
        to an empty schematic.

        Returns
        -------
        delete : bool
            True if parent should delete this instance.
        """
        return False

    def get_schematic_parameters(self):
        # type: () -> Dict[str, str]
        """Returns the schematic parameter dictionary of this instance.

        NOTE: This method is only used by BAG primitives, as they are
        implemented with parameterized cells in the CAD database.  Custom
        subclasses should not override this method.

        Returns
        -------
        params : Dict[str, str]
            the schematic parameter dictionary.
        """
        return {}

    def get_cell_name_from_parameters(self):
        """Returns new cell name based on parameters.

        NOTE: This method is only used by BAG primitives.  This method
        enables a BAG primitive to change the cell master based on
        design parameters (e.g. change transistor instance based on the
        intent parameter).  Custom subclasses should not override this
        method.

        Returns
        -------
        cell : str
            the cell name based on parameters.
        """
        return super(Module, self).cell_name

    def rename_pin(self, old_pin, new_pin):
        # type: (str, str) -> None
        """Renames an input/output pin of this schematic.

        NOTE: Make sure to call :meth:`.reconnect_instance_terminal` so that instances are
        connected to the new pin.

        Parameters
        ----------
   
Download .txt
gitextract_e9aig5le/

├── .gitignore
├── .gitmodules
├── LICENSE
├── README.md
├── bag/
│   ├── LICENSE
│   ├── __init__.py
│   ├── concurrent/
│   │   ├── LICENSE
│   │   ├── __init__.py
│   │   └── core.py
│   ├── core.py
│   ├── data/
│   │   ├── LICENSE
│   │   ├── __init__.py
│   │   ├── core.py
│   │   ├── dc.py
│   │   ├── digital.py
│   │   ├── lti.py
│   │   ├── ltv.py
│   │   ├── mos.py
│   │   └── plot.py
│   ├── design/
│   │   ├── LICENSE
│   │   ├── __init__.py
│   │   └── module.py
│   ├── interface/
│   │   ├── LICENSE
│   │   ├── __init__.py
│   │   ├── base.py
│   │   ├── database.py
│   │   ├── ocean.py
│   │   ├── server.py
│   │   ├── simulator.py
│   │   ├── skill.py
│   │   ├── templates/
│   │   │   ├── LICENSE
│   │   │   ├── Module.pyi
│   │   │   ├── PrimModule.pyi
│   │   │   ├── calibreview_setup.txt
│   │   │   ├── load_results.ocn
│   │   │   └── run_simulation.ocn
│   │   └── zmqwrapper.py
│   ├── io/
│   │   ├── LICENSE
│   │   ├── __init__.py
│   │   ├── common.py
│   │   ├── file.py
│   │   ├── gui.py
│   │   ├── process.py
│   │   ├── sim_data.py
│   │   └── template.py
│   ├── layout/
│   │   ├── LICENSE
│   │   ├── __init__.py
│   │   ├── core.py
│   │   ├── digital.py
│   │   ├── objects.py
│   │   ├── routing/
│   │   │   ├── LICENSE
│   │   │   ├── __init__.py
│   │   │   ├── base.py
│   │   │   ├── fill.py
│   │   │   └── grid.py
│   │   ├── tech.py
│   │   ├── template.py
│   │   └── util.py
│   ├── math/
│   │   ├── LICENSE
│   │   ├── __init__.py
│   │   ├── dfun.py
│   │   └── interpolate.py
│   ├── mdao/
│   │   ├── LICENSE
│   │   ├── __init__.py
│   │   ├── components.py
│   │   └── core.py
│   ├── simulation/
│   │   ├── LICENSE
│   │   ├── __init__.py
│   │   ├── core.py
│   │   └── core_v2.py
│   ├── tech/
│   │   ├── LICENSE
│   │   ├── __init__.py
│   │   ├── core.py
│   │   └── mos.py
│   ├── util/
│   │   ├── LICENSE
│   │   ├── __init__.py
│   │   ├── cache.py
│   │   ├── immutable.py
│   │   ├── interval.py
│   │   ├── parse.py
│   │   └── search.py
│   ├── verification/
│   │   ├── LICENSE
│   │   ├── __init__.py
│   │   ├── base.py
│   │   ├── calibre.py
│   │   ├── icv.py
│   │   ├── pvs.py
│   │   ├── templates/
│   │   │   ├── LICENSE
│   │   │   ├── layout_export_config.txt
│   │   │   └── si_env.txt
│   │   └── virtuoso.py
│   └── virtuoso.py
├── docs/
│   ├── .gitignore
│   ├── LICENSE
│   ├── Makefile
│   ├── README
│   ├── refresh_api.sh
│   └── source/
│       ├── LICENSE
│       ├── api/
│       │   ├── LICENSE
│       │   ├── bag.data.rst
│       │   ├── bag.design.rst
│       │   ├── bag.interface.rst
│       │   ├── bag.io.rst
│       │   ├── bag.layout.routing.rst
│       │   ├── bag.layout.rst
│       │   ├── bag.math.rst
│       │   ├── bag.mdao.rst
│       │   ├── bag.rst
│       │   ├── bag.tech.rst
│       │   ├── bag.util.rst
│       │   ├── bag.verification.rst
│       │   └── modules.rst
│       ├── conf.py
│       ├── developer/
│       │   ├── LICENSE
│       │   └── developer.rst
│       ├── index.rst
│       ├── overview/
│       │   ├── LICENSE
│       │   ├── design.rst
│       │   ├── overview.rst
│       │   ├── schematic.rst
│       │   └── testbench.rst
│       ├── setup/
│       │   ├── LICENSE
│       │   ├── bag_config/
│       │   │   ├── LICENSE
│       │   │   ├── bag_config.rst
│       │   │   ├── database/
│       │   │   │   └── database.rst
│       │   │   ├── misc.rst
│       │   │   ├── simulation/
│       │   │   │   └── simulation.rst
│       │   │   └── socket/
│       │   │       └── socket.rst
│       │   ├── config_summary.rst
│       │   ├── install_python.rst
│       │   ├── new_pdk.rst
│       │   ├── pyoptsparse.rst
│       │   ├── setup.rst
│       │   └── tech_config/
│       │       ├── LICENSE
│       │       ├── layout/
│       │       │   └── layout.rst
│       │       ├── misc.rst
│       │       ├── mos/
│       │       │   └── mos.rst
│       │       └── tech_config.rst
│       └── tutorial/
│           ├── LICENSE
│           ├── figures/
│           │   └── LICENSE
│           └── tutorial.rst
├── run_scripts/
│   ├── LICENSE
│   ├── clean_cds_lib.py
│   ├── compile_verilog.il
│   ├── gen_cell.py
│   ├── generate_verilog.py
│   ├── meas_cell.py
│   ├── run_bag.sh
│   ├── setup_submodules.py
│   ├── sim_cell.py
│   ├── start_bag.il
│   ├── start_bag.sh
│   ├── start_bag_ICADV12d3.il
│   └── virt_server.sh
├── setup.py
└── tests/
    ├── LICENSE
    ├── __init__.py
    └── layout/
        ├── LICENSE
        ├── __init__.py
        └── routing/
            ├── LICENSE
            ├── __init__.py
            └── test_fill.py
Download .txt
SYMBOL INDEX (1515 symbols across 62 files)

FILE: bag/concurrent/core.py
  function batch_async_task (line 17) | def batch_async_task(coro_list):
  class SubProcessManager (line 52) | class SubProcessManager(object):
    method __init__ (line 65) | def __init__(self, max_workers=None, cancel_timeout=10.0):
    method _kill_subprocess (line 75) | async def _kill_subprocess(self, proc: Optional[Process]) -> None:
    method async_new_subprocess (line 105) | async def async_new_subprocess(self,
    method async_new_subprocess_flow (line 155) | async def async_new_subprocess_flow(self,
    method batch_subprocess (line 220) | def batch_subprocess(self, proc_info_list):
    method batch_subprocess_flow (line 253) | def batch_subprocess_flow(self, proc_info_list):

FILE: bag/core.py
  function _get_config_file_abspath (line 36) | def _get_config_file_abspath(fname):
  function _get_port_number (line 53) | def _get_port_number(port_file):
  function _import_class_from_str (line 78) | def _import_class_from_str(class_str):
  class Testbench (line 100) | class Testbench(object):
    method __init__ (line 133) | def __init__(self,  # type: Testbench
    method get_defined_simulation_environments (line 158) | def get_defined_simulation_environments(self):
    method get_current_simulation_environments (line 163) | def get_current_simulation_environments(self):
    method add_output (line 168) | def add_output(self, var, expr):
    method set_parameter (line 183) | def set_parameter(self, name, val, precision=6):
    method set_env_parameter (line 202) | def set_env_parameter(self, name, val_list, precision=6):
    method set_sweep_parameter (line 234) | def set_sweep_parameter(self, name, precision=6, **kwargs):
    method set_simulation_environments (line 277) | def set_simulation_environments(self, env_list):
    method set_simulation_view (line 290) | def set_simulation_view(self, lib_name, cell_name, sim_view):
    method update_testbench (line 311) | def update_testbench(self):
    method run_simulation (line 328) | def run_simulation(self, precision=6, sim_tag=None):
    method load_sim_results (line 348) | def load_sim_results(self, hist_name, precision=6):
    method async_run_simulation (line 368) | async def async_run_simulation(self,
    method async_load_results (line 390) | async def async_load_results(self, hist_name: str, precision: int = 6)...
  function create_tech_info (line 411) | def create_tech_info(bag_config_path=None):
  class BagProject (line 432) | class BagProject(object):
    method __init__ (line 454) | def __init__(self, bag_config_path=None, port=None):
    method default_lib_path (line 503) | def default_lib_path(self):
    method close_bag_server (line 507) | def close_bag_server(self):
    method close_sim_server (line 514) | def close_sim_server(self):
    method import_design_library (line 521) | def import_design_library(self, lib_name):
    method import_sch_cellview (line 536) | def import_sch_cellview(self, lib_name: str, cell_name: str) -> None:
    method get_cells_in_library (line 554) | def get_cells_in_library(self, lib_name):
    method make_template_db (line 575) | def make_template_db(self, impl_lib, grid_specs, use_cybagoa=True, gds...
    method generate_cell (line 606) | def generate_cell(self,  # type: BagProject
    method replace_dut_in_wrapper (line 752) | def replace_dut_in_wrapper(self, params: Dict[str, Any], dut_lib: str,
    method simulate_cell (line 763) | def simulate_cell(self,
    method measure_cell (line 951) | def measure_cell(self,
    method create_library (line 1021) | def create_library(self, lib_name, lib_path=''):
    method create_design_module (line 1038) | def create_design_module(self, lib_name, cell_name, **kwargs):
    method new_schematic_instance (line 1058) | def new_schematic_instance(self, lib_name='', cell_name='', params=Non...
    method clear_schematic_database (line 1095) | def clear_schematic_database(self):
    method instantiate_schematic (line 1100) | def instantiate_schematic(self, lib_name, content_list, lib_path=''):
    method batch_schematic (line 1120) | def batch_schematic(self,  # type: BagProject
    method configure_testbench (line 1156) | def configure_testbench(self, tb_lib, tb_cell):
    method load_testbench (line 1183) | def load_testbench(self, tb_lib, tb_cell):
    method instantiate_layout_pcell (line 1208) | def instantiate_layout_pcell(self, lib_name, cell_name, inst_lib, inst...
    method instantiate_layout (line 1237) | def instantiate_layout(self, lib_name, view_name, via_tech, layout_list):
    method release_write_locks (line 1257) | def release_write_locks(self, lib_name, cell_view_list):
    method run_lvs (line 1273) | def run_lvs(self,  # type: BagProject
    method run_rcx (line 1306) | def run_rcx(self,  # type: BagProject
    method export_layout (line 1357) | def export_layout(self, lib_name, cell_name, out_file, **kwargs):
    method batch_export_layout (line 1386) | def batch_export_layout(self, info_list):
    method async_run_lvs (line 1421) | async def async_run_lvs(self, lib_name: str, cell_name: str, **kwargs:...
    method async_run_rcx (line 1446) | async def async_run_rcx(self,  # type: BagProject
    method create_schematic_from_netlist (line 1487) | def create_schematic_from_netlist(self, netlist, lib_name, cell_name,
    method create_verilog_view (line 1513) | def create_verilog_view(self, verilog_file, lib_name, cell_name, **kwa...

FILE: bag/data/core.py
  class Waveform (line 12) | class Waveform(object):
    method __init__ (line 31) | def __init__(self, xvec, yvec, xtol, order=3, ext=3):
    method xvec (line 40) | def xvec(self):
    method yvec (line 45) | def yvec(self):
    method order (line 50) | def order(self):
    method xtol (line 55) | def xtol(self):
    method ext (line 60) | def ext(self):
    method __call__ (line 64) | def __call__(self, *arg, **kwargs):
    method get_xrange (line 68) | def get_xrange(self):
    method shift_by (line 80) | def shift_by(self, xshift):
    method get_all_crossings (line 95) | def get_all_crossings(self, threshold, start=None, stop=None, edge='bo...
    method get_crossing (line 158) | def get_crossing(self, threshold, start=None, stop=None, n=1, edge='bo...
    method to_arrays (line 184) | def to_arrays(self, xmin=None, xmax=None):
    method get_eye_specs (line 214) | def get_eye_specs(self, tbit, tsample, thres=0.0, nlev=2):
    method _add_xy (line 306) | def _add_xy(self, other):
    method __add__ (line 316) | def __add__(self, other):
    method __neg__ (line 326) | def __neg__(self):
    method __mul__ (line 329) | def __mul__(self, scale):
    method __rmul__ (line 334) | def __rmul__(self, scale):

FILE: bag/data/dc.py
  class DCCircuit (line 15) | class DCCircuit(object):
    method __init__ (line 26) | def __init__(self, ndb, pdb):
    method _get_node_id (line 36) | def _get_node_id(self, name):
    method set_voltage_source (line 47) | def set_voltage_source(self, node_name, voltage):
    method add_transistor (line 62) | def add_transistor(self, d_name, g_name, s_name, b_name, mos_type, int...
    method solve (line 108) | def solve(self, env, guess_dict, itol=1e-10, inorm=1e-6):

FILE: bag/data/digital.py
  function de_bruijn (line 13) | def de_bruijn(n, symbols=None):
  function dig_to_pwl (line 55) | def dig_to_pwl(values, tper, trf, td=0):
  function get_crossing_index (line 120) | def get_crossing_index(yvec, threshold, n=0, rising=True):
  function get_flop_timing (line 150) | def get_flop_timing(tvec, d, q, clk, ttol, data_thres=0.5,

FILE: bag/data/lti.py
  class LTICircuit (line 16) | class LTICircuit(object):
    method __init__ (line 31) | def __init__(self, udot_tol=1e-12):
    method _get_node_id (line 41) | def _get_node_id(self, name):
    method _add (line 52) | def _add(mat, key, val):
    method add_res (line 59) | def add_res(self, res, p_name, n_name):
    method add_conductance (line 77) | def add_conductance(self, g, p_name, n_name):
    method add_vccs (line 104) | def add_vccs(self, gm, p_name, n_name, cp_name, cn_name='gnd'):
    method add_vcvs (line 140) | def add_vcvs(self, gain, p_name, n_name, cp_name, cn_name='gnd'):
    method add_cap (line 172) | def add_cap(self, cap, p_name, n_name):
    method add_ind (line 199) | def add_ind(self, ind, p_name, n_name):
    method add_transistor (line 227) | def add_transistor(self, tran_info, d_name, g_name, s_name, b_name='gn...
    method _count_rank (line 281) | def _count_rank(cls, diag):
    method _solve_gx_bw (line 290) | def _solve_gx_bw(cls, g, b):
    method _transform_c_qr (line 336) | def _transform_c_qr(cls, g, c, b, d):
    method _reduce_state_space (line 353) | def _reduce_state_space(cls, g, c, b, d, e, ndim_w):
    method _simplify (line 390) | def _simplify(cls, g, c, b, d, e, ndim_w):
    method _build_mna_matrices (line 401) | def _build_mna_matrices(self, inputs, outputs, in_type='v'):
    method get_state_space (line 533) | def get_state_space(self, inputs, outputs, in_type='v'):
    method get_num_den (line 567) | def get_num_den(self, in_name, out_name, in_type='v', atol=0.0):
    method get_transfer_function (line 600) | def get_transfer_function(self, in_name, out_name, in_type='v', atol=0...
    method get_impedance (line 623) | def get_impedance(self, node_name, freq, atol=0.0):
  function get_w_crossings (line 648) | def get_w_crossings(num, den, atol=1e-8):
  function get_w_3db (line 727) | def get_w_3db(num, den, atol=1e-8):
  function get_stability_margins (line 793) | def get_stability_margins(num, den, rtol=1e-8, atol=1e-8):

FILE: bag/data/ltv.py
  function _even_quotient (line 11) | def _even_quotient(a, b, tol=1e-6):
  class LTVImpulseFinite (line 19) | class LTVImpulseFinite(object):
    method __init__ (line 93) | def __init__(self, hmat, m, n, tper, k, out0):
    method _print_debug_msg (line 116) | def _print_debug_msg(result):
    method __call__ (line 124) | def __call__(self, t, tau, debug=False):
    method _get_core (line 164) | def _get_core(self, num_points, debug=False):
    method visualize (line 189) | def visualize(self, fig_idx, num_points, num_period,
    method lsim (line 250) | def lsim(self, u, tstep, tstart=0.0, ac_only=False, periodic=False, de...
    method lsim_digital (line 341) | def lsim_digital(self, tsym, tstep, data, pulse, tstart=0.0, nchain=1,...

FILE: bag/data/mos.py
  function mos_y_to_ss (line 11) | def mos_y_to_ss(sim_data, char_freq, fg, ibias, cfit_method='average'):

FILE: bag/data/plot.py
  function figure (line 25) | def figure(fig_id, picker=5.0):
  function plot_waveforms (line 43) | def plot_waveforms(xvec, panel_list, fig=1):
  function _fpart (line 81) | def _fpart(x):
  function _rfpart (line 85) | def _rfpart(x):
  function draw_line (line 89) | def draw_line(x0, y0, x1, y1, xmax, grid):
  function plot_eye_heatmap (line 133) | def plot_eye_heatmap(fig, tvec, yvec, tper, tstart=None, tend=None, toff...
  function plot_eye (line 223) | def plot_eye(fig, tvec, yvec_list, tper, tstart=None, tend=None,
  function _find_closest_point (line 298) | def _find_closest_point(x, y, xvec, yvec, xnorm, ynorm):
  class WaveformPlotter (line 321) | class WaveformPlotter(object):
    method __init__ (line 339) | def __init__(self, fig_idx, picker=5.0, normal_width=1.5, select_width...
    method plot (line 352) | def plot(self, *args, **kwargs):
    method setup (line 363) | def setup(self):
    method figure_closed (line 395) | def figure_closed(self, event):
    method figure_resized (line 405) | def figure_resized(self, event):
    method fix_legend_location (line 410) | def fix_legend_location(self, event):
    method legend_line_picked (line 422) | def legend_line_picked(self, artist):
    method legend_text_picked (line 434) | def legend_text_picked(self, artist, draw=True):
  class MarkerFigure (line 456) | class MarkerFigure(Figure):
    method __init__ (line 457) | def __init__(self, **kwargs):
    method set_line_visibility (line 467) | def set_line_visibility(self, axline, visible):
    method register_pick_event (line 481) | def register_pick_event(self, artist_set, fun):
    method on_button_release (line 485) | def on_button_release(self, event):
    method on_motion (line 490) | def on_motion(self, event):
    method _get_idx_under_point (line 511) | def _get_idx_under_point(self, event):
    method on_pick (line 531) | def on_pick(self, event):
    method _create_marker (line 582) | def _create_marker(self):
    method close_figure (line 604) | def close_figure(self):
    method setup_callbacks (line 607) | def setup_callbacks(self):

FILE: bag/design/module.py
  class ModuleDB (line 20) | class ModuleDB(MasterDB):
    method __init__ (line 44) | def __init__(self, lib_defs, tech_info, sch_exc_libs, prj=None, name_p...
    method create_master_instance (line 55) | def create_master_instance(self, gen_cls, lib_name, params, used_cell_...
    method create_masters_in_db (line 86) | def create_masters_in_db(self, lib_name, content_list, debug=False):
    method tech_info (line 105) | def tech_info(self):
    method is_lib_excluded (line 110) | def is_lib_excluded(self, lib_name):
  class SchInstance (line 127) | class SchInstance(object):
    method __init__ (line 150) | def __init__(self,
    method change_generator (line 170) | def change_generator(self, gen_lib_name, gen_cell_name, static=False):
    method name (line 193) | def name(self):
    method connections (line 199) | def connections(self):
    method is_primitive (line 205) | def is_primitive(self):
    method should_delete (line 216) | def should_delete(self):
    method master (line 222) | def master(self):
    method master_cell_name (line 227) | def master_cell_name(self):
    method master_key (line 233) | def master_key(self):
    method copy (line 237) | def copy(self, inst_name, connections=None):
    method get_master_lib_name (line 259) | def get_master_lib_name(self, impl_lib):
    method design_specs (line 275) | def design_specs(self, *args, **kwargs):
    method design (line 280) | def design(self, *args, **kwargs):
    method _update_master (line 285) | def _update_master(self, design_fun, args, kwargs):
    method implement_design (line 303) | def implement_design(self, lib_name, top_cell_name='', prefix='', suff...
    method get_layout_params (line 345) | def get_layout_params(self, **kwargs):
  class Module (line 354) | class Module(DesignMaster, metaclass=abc.ABCMeta):
    method __init__ (line 377) | def __init__(self, database, yaml_fname, **kwargs):
    method pin_list (line 417) | def pin_list(self):
    method design (line 422) | def design(self, **kwargs):
    method finalize (line 442) | def finalize(self):
    method get_params_info (line 479) | def get_params_info(cls):
    method get_master_basename (line 490) | def get_master_basename(self):
    method get_content (line 501) | def get_content(self, lib_name, rename_fun):
    method cell_name (line 543) | def cell_name(self):
    method orig_cell_name (line 551) | def orig_cell_name(self):
    method is_primitive (line 556) | def is_primitive(self):
    method should_delete_instance (line 571) | def should_delete_instance(self):
    method get_schematic_parameters (line 586) | def get_schematic_parameters(self):
    method get_cell_name_from_parameters (line 601) | def get_cell_name_from_parameters(self):
    method rename_pin (line 617) | def rename_pin(self, old_pin, new_pin):
    method add_pin (line 633) | def add_pin(self, new_pin, pin_type):
    method remove_pin (line 649) | def remove_pin(self, remove_pin):
    method delete_instance (line 660) | def delete_instance(self, inst_name):
    method replace_instance_master (line 671) | def replace_instance_master(self, inst_name, lib_name, cell_name, stat...
    method reconnect_instance_terminal (line 704) | def reconnect_instance_terminal(self, inst_name, term_name, net_name, ...
    method array_instance (line 756) | def array_instance(self, inst_name, inst_name_list, term_list=None):
    method design_dc_bias_sources (line 790) | def design_dc_bias_sources(self,  # type: Module
    method design_dummy_transistors (line 862) | def design_dummy_transistors(self, dum_info, inst_name, vdd_name, vss_...
  class MosModuleBase (line 911) | class MosModuleBase(Module):
    method __init__ (line 924) | def __init__(self, database, yaml_file, **kwargs):
    method get_params_info (line 928) | def get_params_info(cls):
    method design (line 937) | def design(self, w=1e-6, l=60e-9, nf=1, intent='standard'):
    method get_schematic_parameters (line 940) | def get_schematic_parameters(self):
    method get_cell_name_from_parameters (line 953) | def get_cell_name_from_parameters(self):
    method is_primitive (line 958) | def is_primitive(self):
    method should_delete_instance (line 962) | def should_delete_instance(self):
  class ResPhysicalModuleBase (line 967) | class ResPhysicalModuleBase(Module):
    method __init__ (line 980) | def __init__(self, database, yaml_file, **kwargs):
    method get_params_info (line 984) | def get_params_info(cls):
    method design (line 992) | def design(self, w=1e-6, l=1e-6, intent='standard'):
    method get_schematic_parameters (line 995) | def get_schematic_parameters(self):
    method get_cell_name_from_parameters (line 1004) | def get_cell_name_from_parameters(self):
    method is_primitive (line 1008) | def is_primitive(self):
    method should_delete_instance (line 1012) | def should_delete_instance(self):
  class ResMetalModule (line 1017) | class ResMetalModule(Module):
    method __init__ (line 1030) | def __init__(self, database, yaml_file, **kwargs):
    method get_params_info (line 1034) | def get_params_info(cls):
    method design (line 1042) | def design(self, w, l, layer):
    method get_schematic_parameters (line 1046) | def get_schematic_parameters(self):
    method is_primitive (line 1056) | def is_primitive(self):
    method should_delete_instance (line 1060) | def should_delete_instance(self):

FILE: bag/interface/base.py
  class InterfaceBase (line 11) | class InterfaceBase:
    method __init__ (line 16) | def __init__(self):
    method render_file_template (line 19) | def render_file_template(self, temp_name, params):

FILE: bag/interface/database.py
  function dict_to_item_list (line 21) | def dict_to_item_list(table):
  function format_inst_map (line 37) | def format_inst_map(inst_map):
  class DbAccess (line 62) | class DbAccess(InterfaceBase, abc.ABC):
    method __init__ (line 73) | def __init__(self, tmp_dir, db_config):
    method get_default_lib_path (line 95) | def get_default_lib_path(cls, db_config):
    method default_lib_path (line 104) | def default_lib_path(self):
    method close (line 115) | def close(self):
    method parse_schematic_template (line 121) | def parse_schematic_template(self, lib_name, cell_name):
    method get_cells_in_library (line 139) | def get_cells_in_library(self, lib_name):
    method create_library (line 157) | def create_library(self, lib_name, lib_path=''):
    method create_implementation (line 170) | def create_implementation(self, lib_name, template_list, change_list, ...
    method configure_testbench (line 187) | def configure_testbench(self, tb_lib, tb_cell):
    method get_testbench_info (line 211) | def get_testbench_info(self, tb_lib, tb_cell):
    method update_testbench (line 235) | def update_testbench(self,  # type: DbAccess
    method instantiate_layout_pcell (line 264) | def instantiate_layout_pcell(self, lib_name, cell_name, view_name,
    method instantiate_layout (line 288) | def instantiate_layout(self, lib_name, view_name, via_tech, layout_list):
    method release_write_locks (line 306) | def release_write_locks(self, lib_name, cell_view_list):
    method create_schematic_from_netlist (line 320) | def create_schematic_from_netlist(self, netlist, lib_name, cell_name,
    method create_verilog_view (line 343) | def create_verilog_view(self, verilog_file, lib_name, cell_name, **kwa...
    method get_python_template (line 360) | def get_python_template(self, lib_name, cell_name, primitive_table):
    method _process_rcx_output (line 408) | def _process_rcx_output(self, netlist, log_fname, lib_name, cell_name,...
    method async_run_lvs (line 419) | async def async_run_lvs(self, lib_name: str, cell_name: str, **kwargs:...
    method async_run_rcx (line 445) | async def async_run_rcx(self,  # type: DbAccess
    method async_export_layout (line 491) | async def async_export_layout(self, lib_name: str, cell_name: str,
    method import_design_library (line 519) | def import_design_library(self, lib_name, dsn_db, new_lib_path):
    method import_sch_cellview (line 535) | def import_sch_cellview(self, lib_name, cell_name, dsn_db, new_lib_path):
    method _import_design (line 554) | def _import_design(self, lib_name, cell_name, imported_cells, dsn_db, ...
    method instantiate_schematic (line 600) | def instantiate_schematic(self, lib_name, content_list, lib_path=''):

FILE: bag/interface/ocean.py
  class OceanInterface (line 17) | class OceanInterface(SimProcessManager):
    method __init__ (line 28) | def __init__(self, tmp_dir, sim_config):
    method format_parameter_value (line 34) | def format_parameter_value(self, param_config, precision):
    method _get_ocean_info (line 86) | def _get_ocean_info(self, save_dir, script_fname, log_fname):
    method setup_sim_process (line 102) | def setup_sim_process(self, lib, cell, outputs, precision, sim_tag):
    method setup_load_process (line 140) | def setup_load_process(self, lib, cell, hist_name, outputs, precision):

FILE: bag/interface/server.py
  function _object_to_skill_file_helper (line 31) | def _object_to_skill_file_helper(py_obj, file_obj):
  function object_to_skill_file (line 75) | def object_to_skill_file(py_obj, file_obj):
  class SkillServer (line 97) | class SkillServer(object):
    method __init__ (line 118) | def __init__(self, router, virt_in, virt_out, tmpdir=None):
    method run (line 128) | def run(self):
    method send_skill (line 152) | def send_skill(self, expr):
    method recv_skill (line 163) | def recv_skill(self):
    method close (line 171) | def close(self):
    method process_skill_request (line 175) | def process_skill_request(self, request):
    method process_skill_result (line 228) | def process_skill_result(self, msg, out_file=None):

FILE: bag/interface/simulator.py
  class SimAccess (line 18) | class SimAccess(InterfaceBase, abc.ABC):
    method __init__ (line 29) | def __init__(self, tmp_dir, sim_config):
    method format_parameter_value (line 37) | def format_parameter_value(self, param_config, precision):
    method async_run_simulation (line 75) | async def async_run_simulation(self, tb_lib, tb_cell, outputs, precisi...
    method async_load_results (line 100) | async def async_load_results(self, lib, cell, hist_name, outputs, prec...
  class SimProcessManager (line 128) | class SimProcessManager(SimAccess, metaclass=abc.ABCMeta):
    method __init__ (line 139) | def __init__(self, tmp_dir, sim_config):
    method setup_sim_process (line 149) | def setup_sim_process(self, lib, cell, outputs, precision, sim_tag):
    method setup_load_process (line 182) | def setup_load_process(self, lib, cell, hist_name, outputs, precision):
    method async_run_simulation (line 214) | async def async_run_simulation(self, tb_lib: str, tb_cell: str,
    method async_load_results (line 224) | async def async_load_results(self, lib: str, cell: str, hist_name: str,

FILE: bag/interface/skill.py
  function _dict_to_pcell_params (line 22) | def _dict_to_pcell_params(table):
  function to_skill_list_str (line 54) | def to_skill_list_str(pylist):
  function _handle_reply (line 72) | def _handle_reply(reply):
  class VirtuosoException (line 88) | class VirtuosoException(Exception):
    method __init__ (line 91) | def __init__(self, *args, **kwargs):
  class SkillInterface (line 96) | class SkillInterface(DbAccess):
    method __init__ (line 112) | def __init__(self, dealer, tmp_dir, db_config):
    method close (line 119) | def close(self):
    method _eval_skill (line 125) | def _eval_skill(self, expr, input_files=None, out_file=None):
    method parse_schematic_template (line 178) | def parse_schematic_template(self, lib_name, cell_name):
    method get_cells_in_library (line 196) | def get_cells_in_library(self, lib_name):
    method create_library (line 214) | def create_library(self, lib_name, lib_path=''):
    method create_implementation (line 229) | def create_implementation(self, lib_name, template_list, change_list, ...
    method configure_testbench (line 289) | def configure_testbench(self, tb_lib, tb_cell):
    method get_testbench_info (line 330) | def get_testbench_info(self, tb_lib, tb_cell):
    method update_testbench (line 358) | def update_testbench(self,
    method instantiate_layout_pcell (line 395) | def instantiate_layout_pcell(self, lib_name, cell_name, view_name,
    method instantiate_layout (line 428) | def instantiate_layout(self, lib_name, view_name, via_tech, layout_list):
    method release_write_locks (line 467) | def release_write_locks(self, lib_name, cell_view_list):
    method create_schematic_from_netlist (line 481) | def create_schematic_from_netlist(self, netlist, lib_name, cell_name,
    method get_cell_directory (line 569) | def get_cell_directory(self, lib_name, cell_name):
    method create_verilog_view (line 591) | def create_verilog_view(self, verilog_file, lib_name, cell_name, **kwa...

FILE: bag/interface/templates/Module.pyi
  function __init__ (line 22) | def __init__(self, database, parent=None, prj=None, **kwargs):
  function get_params_info (line 26) | def get_params_info(cls):
  function design (line 38) | def design(self):

FILE: bag/interface/templates/PrimModule.pyi
  function __init__ (line 18) | def __init__(self, database, parent=None, prj=None, **kwargs):

FILE: bag/interface/zmqwrapper.py
  class ZMQDealer (line 17) | class ZMQDealer(object):
    method __init__ (line 37) | def __init__(self, port, pipeline=100, host='localhost', log_file=None):
    method log_msg (line 61) | def log_msg(self, msg):
    method log_obj (line 66) | def log_obj(self, msg, obj):
    method close (line 72) | def close(self):
    method send_obj (line 76) | def send_obj(self, obj):
    method recv_obj (line 89) | def recv_obj(self, timeout=None, enable_cancel=False):
    method recv_msg (line 128) | def recv_msg(self):
  class ZMQRouter (line 141) | class ZMQRouter(object):
    method __init__ (line 164) | def __init__(self, port=None, min_port=5000, max_port=9999, pipeline=1...
    method get_port (line 189) | def get_port(self):
    method is_closed (line 193) | def is_closed(self):
    method close (line 197) | def close(self):
    method log_msg (line 201) | def log_msg(self, msg):
    method log_obj (line 206) | def log_obj(self, msg, obj):
    method send_msg (line 212) | def send_msg(self, msg, addr=None):
    method send_obj (line 230) | def send_obj(self, obj, addr=None):
    method poll_for_read (line 250) | def poll_for_read(self, timeout):
    method recv_obj (line 265) | def recv_obj(self):
    method get_last_sender_addr (line 280) | def get_last_sender_addr(self):

FILE: bag/io/common.py
  function fix_string (line 14) | def fix_string(obj):
  function to_bytes (line 37) | def to_bytes(my_str):
  function set_encoding (line 53) | def set_encoding(new_encoding):
  function get_encoding (line 67) | def get_encoding():
  function set_error_policy (line 78) | def set_error_policy(new_policy):
  function get_error_policy (line 91) | def get_error_policy():

FILE: bag/io/file.py
  class Pickle (line 20) | class Pickle:
    method save (line 25) | def save(obj, file, **kwargs) -> None:
    method load (line 30) | def load(file, **kwargs):
  class Yaml (line 35) | class Yaml:
    method save (line 41) | def save(obj, file, **kwargs) -> None:
    method load (line 46) | def load(file, **kwargs):
  function open_file (line 51) | def open_file(fname, mode):
  function read_file (line 73) | def read_file(fname):
  function readlines_iter (line 91) | def readlines_iter(fname):
  function read_yaml_env (line 109) | def read_yaml_env(fname):
  function read_yaml (line 129) | def read_yaml(fname):
  function read_resource (line 148) | def read_resource(package, fname):
  function write_file (line 167) | def write_file(fname, content, append=False, mkdir=True):
  function make_temp_dir (line 191) | def make_temp_dir(prefix, parent_dir=None):
  function open_temp (line 206) | def open_temp(**kwargs):

FILE: bag/io/gui.py
  class StdinThread (line 19) | class StdinThread(QtCore.QThread):
    method __init__ (line 23) | def __init__(self, parent):
    method run (line 27) | def run(self):
  class LogWidget (line 43) | class LogWidget(QtWidgets.QFrame):
    method __init__ (line 51) | def __init__(self, parent=None):
    method clear_log (line 70) | def clear_log(self):
    method save_log (line 74) | def save_log(self):
    method print_file (line 80) | def print_file(self, file_obj):
  class LogViewer (line 93) | class LogViewer(QtWidgets.QWidget):
    method __init__ (line 96) | def __init__(self):
    method closeEvent (line 133) | def closeEvent(self, evt):
    method parse_cmd (line 140) | def parse_cmd(self, cmd):
    method change_log (line 154) | def change_log(self, new_idx):
    method update_logfile (line 172) | def update_logfile(self, fname):
    method remove_log (line 177) | def remove_log(self, log_tag):
    method add_log (line 183) | def add_log(self, log_tag, log_file):
  function app_start (line 190) | def app_start():
  function start_viewer (line 199) | def start_viewer():
  function add_log (line 208) | def add_log(proc, tag, fname):
  function remove_log (line 219) | def remove_log(proc, tag):
  function close (line 230) | def close(proc):

FILE: bag/io/process.py
  function run_proc_with_quit (line 26) | def run_proc_with_quit(proc_id, quit_dict, args, logfile=None, append=Fa...
  function run_and_wait (line 54) | def run_and_wait(args, timeout=None, logfile=None, append=False,
  class ProcessManager (line 98) | class ProcessManager(object):
    method __init__ (line 113) | def __init__(self, max_workers=None, cancel_timeout=10.0):
    method close (line 123) | def close(self, timeout=10.0):
    method new_thread (line 137) | def new_thread(self, fun, basename=None, callback=None):
    method new_process (line 182) | def new_process(self, args, basename=None, logfile=None, append=False,
    method _get_output (line 234) | def _get_output(future, timeout=None):
    method cancel (line 244) | def cancel(self, proc_id, timeout=None):
    method done (line 294) | def done(self, proc_id):
    method wait (line 309) | def wait(self, proc_id, timeout=None, cancel_timeout=None):
    method _start_cmd (line 349) | def _start_cmd(self, args, proc_id, logfile=None, append=False, env=No...

FILE: bag/io/sim_data.py
  class SweepArray (line 20) | class SweepArray(np.ndarray):
    method __new__ (line 24) | def __new__(cls, data, sweep_params=None):
    method __array_finalize__ (line 33) | def __array_finalize__(self, obj):
    method __reduce__ (line 39) | def __reduce__(self):
    method __setstate__ (line 48) | def __setstate__(self, state):
  function _get_sweep_params (line 55) | def _get_sweep_params(fname):
  function load_sim_results (line 105) | def load_sim_results(save_dir):
  function save_sim_results (line 182) | def save_sim_results(results, fname, compression='gzip'):
  function load_sim_file (line 224) | def load_sim_file(fname):

FILE: bag/io/template.py
  function new_template_env (line 9) | def new_template_env(parent_package, tmp_folder):

FILE: bag/layout/core.py
  class TechInfo (line 26) | class TechInfo(object, metaclass=abc.ABCMeta):
    method __init__ (line 49) | def __init__(self, res, layout_unit, via_tech, process_params):
    method get_well_layers (line 56) | def get_well_layers(self, sub_type):
    method get_implant_layers (line 62) | def get_implant_layers(self, mos_type, res_type=None):
    method get_threshold_layers (line 81) | def get_threshold_layers(self, mos_type, threshold, res_type=None):
    method get_exclude_layer (line 87) | def get_exclude_layer(self, layer_id):
    method get_dnw_margin_unit (line 93) | def get_dnw_margin_unit(self, dnw_mode):
    method get_dnw_layers (line 110) | def get_dnw_layers(self):
    method get_res_metal_layers (line 122) | def get_res_metal_layers(self, layer_id):
    method get_metal_dummy_layers (line 139) | def get_metal_dummy_layers(self, layer_id):
    method add_cell_boundary (line 156) | def add_cell_boundary(self, template, box):
    method draw_device_blockage (line 171) | def draw_device_blockage(self, template):
    method get_via_drc_info (line 182) | def get_via_drc_info(self, vname, vtype, mtype, mw_unit, is_bot):
    method get_min_space (line 228) | def get_min_space(self, layer_type, width, unit_mode=False, same_color...
    method get_min_line_end_space (line 250) | def get_min_line_end_space(self, layer_type, width, unit_mode=False):
    method get_min_length (line 270) | def get_min_length(self, layer_type, width):
    method get_layer_id (line 289) | def get_layer_id(self, layer_name):
    method get_layer_name (line 305) | def get_layer_name(self, layer_id):
    method get_layer_type (line 322) | def get_layer_type(self, layer_name):
    method get_via_name (line 338) | def get_via_name(self, bot_layer_id):
    method get_metal_em_specs (line 354) | def get_metal_em_specs(self, layer_name, w, l=-1, vertical=False, **kw...
    method get_via_em_specs (line 383) | def get_via_em_specs(self, via_name,  # type: str
    method get_res_rsquare (line 427) | def get_res_rsquare(self, res_type):
    method get_res_width_bounds (line 445) | def get_res_width_bounds(self, res_type):
    method get_res_length_bounds (line 463) | def get_res_length_bounds(self, res_type):
    method get_res_min_nsquare (line 481) | def get_res_min_nsquare(self, res_type):
    method get_res_em_specs (line 497) | def get_res_em_specs(self, res_type, w, l=-1, **kwargs):
    method via_tech_name (line 525) | def via_tech_name(self):
    method pin_purpose (line 530) | def pin_purpose(self):
    method resolution (line 535) | def resolution(self):
    method layout_unit (line 540) | def layout_unit(self):
    method merge_well (line 544) | def merge_well(self, template, inst_list, sub_type, threshold=None, re...
    method use_flip_parity (line 565) | def use_flip_parity(self):
    method finalize_template (line 570) | def finalize_template(self, template):
    method get_res_info (line 582) | def get_res_info(self, res_type, w, l, **kwargs):
    method get_via_types (line 622) | def get_via_types(self, bmtype, tmtype):
    method get_best_via_array (line 625) | def get_best_via_array(self, vname, bmtype, tmtype, bot_dir, top_dir, ...
    method _via_better (line 851) | def _via_better(self, mdim_list1, mdim_list2):
    method get_via_id (line 865) | def get_via_id(self, bot_layer, top_layer):
    method get_via_info (line 884) | def get_via_info(self, bbox, bot_layer, top_layer, bot_dir, bot_len=-1...
    method design_resistor (line 1019) | def design_resistor(self, res_type, res_targ, idc=0.0, iac_rms=0.0,
  class DummyTechInfo (line 1124) | class DummyTechInfo(TechInfo):
    method __init__ (line 1133) | def __init__(self, tech_params):
    method get_well_layers (line 1136) | def get_well_layers(self, sub_type):
    method get_implant_layers (line 1139) | def get_implant_layers(self, mos_type, res_type=None):
    method get_threshold_layers (line 1142) | def get_threshold_layers(self, mos_type, threshold, res_type=None):
    method get_dnw_layers (line 1145) | def get_dnw_layers(self):
    method get_exclude_layer (line 1149) | def get_exclude_layer(self, layer_id):
    method get_dnw_margin_unit (line 1154) | def get_dnw_margin_unit(self, dnw_mode):
    method get_res_metal_layers (line 1158) | def get_res_metal_layers(self, layer_id):
    method get_metal_dummy_layers (line 1162) | def get_metal_dummy_layers(self, layer_id):
    method add_cell_boundary (line 1166) | def add_cell_boundary(self, template, box):
    method draw_device_blockage (line 1169) | def draw_device_blockage(self, template):
    method get_via_drc_info (line 1172) | def get_via_drc_info(self, vname, vtype, mtype, mw_unit, is_bot):
    method get_min_space (line 1175) | def get_min_space(self, layer_type, width, unit_mode=False, same_color...
    method get_min_line_end_space (line 1178) | def get_min_line_end_space(self, layer_type, width, unit_mode=False):
    method get_min_length (line 1181) | def get_min_length(self, layer_type, width):
    method get_layer_id (line 1184) | def get_layer_id(self, layer_name):
    method get_layer_name (line 1187) | def get_layer_name(self, layer_id):
    method get_layer_type (line 1190) | def get_layer_type(self, layer_name):
    method get_via_name (line 1193) | def get_via_name(self, bot_layer_id):
    method get_metal_em_specs (line 1196) | def get_metal_em_specs(self, layer_name, w, l=-1, vertical=False, **kw...
    method get_via_em_specs (line 1199) | def get_via_em_specs(self, via_name, bm_layer, tm_layer, via_type='squ...
    method get_res_rsquare (line 1203) | def get_res_rsquare(self, res_type):
    method get_res_width_bounds (line 1206) | def get_res_width_bounds(self, res_type):
    method get_res_length_bounds (line 1209) | def get_res_length_bounds(self, res_type):
    method get_res_min_nsquare (line 1212) | def get_res_min_nsquare(self, res_type):
    method get_res_em_specs (line 1215) | def get_res_em_specs(self, res_type, w, l=-1, **kwargs):
  class BagLayout (line 1219) | class BagLayout(object):
    method __init__ (line 1230) | def __init__(self, grid, use_cybagoa=False):
    method pin_purpose (line 1253) | def pin_purpose(self):
    method is_empty (line 1258) | def is_empty(self):
    method inst_iter (line 1262) | def inst_iter(self):
    method finalize (line 1266) | def finalize(self):
    method get_rect_bbox (line 1321) | def get_rect_bbox(self, layer):
    method get_masters_set (line 1340) | def get_masters_set(self):
    method _get_unused_inst_name (line 1344) | def _get_unused_inst_name(self, inst_name):
    method _format_inst (line 1355) | def _format_inst(self, inst):
    method get_content (line 1364) | def get_content(self,  # type: BagLayout
    method add_instance (line 1430) | def add_instance(self, instance):
    method move_all_by (line 1446) | def move_all_by(self, dx=0.0, dy=0.0, unit_mode=False):
    method add_instance_primitive (line 1468) | def add_instance_primitive(self,  # type: BagLayout
    method add_rect (line 1553) | def add_rect(self, rect):
    method add_path (line 1566) | def add_path(self, path):
    method add_polygon (line 1580) | def add_polygon(self, polygon):
    method add_blockage (line 1594) | def add_blockage(self, blockage):
    method add_boundary (line 1608) | def add_boundary(self, boundary):
    method add_via (line 1622) | def add_via(self, via):
    method add_via_primitive (line 1638) | def add_via_primitive(self, via_type, loc, num_rows=1, num_cols=1, sp_...
    method add_pin (line 1705) | def add_pin(self, net_name, layer, bbox, pin_name=None, label=None):
    method add_label (line 1752) | def add_label(self, label, layer, bbox):

FILE: bag/layout/digital.py
  class StdCellBase (line 19) | class StdCellBase(TemplateBase, metaclass=abc.ABCMeta):
    method __init__ (line 37) | def __init__(self, temp_db, lib_name, params, used_names, **kwargs):
    method min_space_width (line 51) | def min_space_width(self):
    method std_col_width (line 57) | def std_col_width(self):
    method std_col_width_unit (line 63) | def std_col_width_unit(self):
    method std_row_height (line 70) | def std_row_height(self):
    method std_row_height_unit (line 76) | def std_row_height_unit(self):
    method std_size (line 83) | def std_size(self):
    method std_routing_layers (line 89) | def std_routing_layers(self):
    method get_num_columns (line 94) | def get_num_columns(self, layer_id, num_tr):
    method set_draw_boundaries (line 114) | def set_draw_boundaries(self, draw_boundaries):
    method get_space_blocks (line 130) | def get_space_blocks(self):
    method get_cell_params (line 135) | def get_cell_params(self, cell_name):
    method set_std_size (line 149) | def set_std_size(self, std_size, top_layer=-1):
    method update_routing_grid (line 185) | def update_routing_grid(self):
    method get_num_tracks (line 201) | def get_num_tracks(self, layer_id):
    method add_std_instance (line 228) | def add_std_instance(self, master, inst_name=None, loc=(0, 0), nx=1, n...
    method draw_boundaries (line 303) | def draw_boundaries(self):
    method fill_space (line 367) | def fill_space(self):
    method add_std_space (line 380) | def add_std_space(self, loc, num_col, update_used_blks=True):
  class StdCellTemplate (line 431) | class StdCellTemplate(StdCellBase):
    method __init__ (line 449) | def __init__(self, temp_db, lib_name, params, used_names, **kwargs):
    method sch_params (line 455) | def sch_params(self):
    method get_params_info (line 459) | def get_params_info(cls):
    method get_layout_basename (line 475) | def get_layout_basename(self):
    method compute_unique_key (line 478) | def compute_unique_key(self):
    method get_sch_master_info (line 482) | def get_sch_master_info(self):
    method draw_layout (line 488) | def draw_layout(self):

FILE: bag/layout/objects.py
  class Figure (line 25) | class Figure(object, metaclass=abc.ABCMeta):
    method __init__ (line 34) | def __init__(self, resolution):
    method transform (line 40) | def transform(self, loc=(0, 0), orient='R0', unit_mode=False, copy=Fal...
    method move_by (line 46) | def move_by(self, dx=0, dy=0, unit_mode=False):
    method resolution (line 62) | def resolution(self):
    method destroyed (line 68) | def destroyed(self):
    method valid (line 74) | def valid(self):
    method check_destroyed (line 79) | def check_destroyed(self):
    method destroy (line 85) | def destroy(self):
  class Arrayable (line 92) | class Arrayable(Figure, metaclass=abc.ABCMeta):
    method __init__ (line 113) | def __init__(self, res, nx=1, ny=1, spx=0, spy=0, unit_mode=False):
    method nx (line 126) | def nx(self):
    method nx (line 132) | def nx(self, val):
    method ny (line 141) | def ny(self):
    method ny (line 147) | def ny(self, val):
    method spx (line 156) | def spx(self):
    method spx (line 162) | def spx(self, val):
    method spx_unit (line 171) | def spx_unit(self):
    method spx_unit (line 177) | def spx_unit(self, val):
    method spy (line 186) | def spy(self):
    method spy (line 192) | def spy(self, val):
    method spy_unit (line 201) | def spy_unit(self):
    method spy_unit (line 207) | def spy_unit(self, val):
    method valid (line 216) | def valid(self):
    method get_item_location (line 221) | def get_item_location(self, row=0, col=0, unit_mode=False):
  class InstanceInfo (line 251) | class InstanceInfo(dict):
    method __init__ (line 258) | def __init__(self, res, change_orient=True, **kwargs):
    method lib (line 292) | def lib(self):
    method cell (line 297) | def cell(self):
    method view (line 302) | def view(self):
    method name (line 307) | def name(self):
    method name (line 312) | def name(self, new_name):
    method loc (line 317) | def loc(self):
    method orient (line 323) | def orient(self):
    method num_rows (line 328) | def num_rows(self):
    method num_cols (line 333) | def num_cols(self):
    method sp_rows (line 338) | def sp_rows(self):
    method sp_cols (line 343) | def sp_cols(self):
    method params (line 348) | def params(self):
    method params (line 353) | def params(self, new_params):
    method master_key (line 358) | def master_key(self):
    method master_key (line 362) | def master_key(self, value):
    method angle_reflect (line 366) | def angle_reflect(self):
    method copy (line 388) | def copy(self):
    method move_by (line 392) | def move_by(self, dx=0, dy=0):
  class Instance (line 409) | class Instance(Arrayable):
    method __init__ (line 438) | def __init__(self,
    method new_master_with (line 464) | def new_master_with(self, **kwargs):
    method blockage_iter (line 479) | def blockage_iter(self, layer_id, test_box, spx=0, spy=0):
    method all_rect_iter (line 515) | def all_rect_iter(self):
    method intersection_rect_iter (line 532) | def intersection_rect_iter(self, layer_id, test_box):
    method get_rect_bbox (line 563) | def get_rect_bbox(self, layer):
    method track_bbox_iter (line 575) | def track_bbox_iter(self):
    method master (line 582) | def master(self):
    method location (line 588) | def location(self):
    method location (line 594) | def location(self, new_loc):
    method location_unit (line 602) | def location_unit(self):
    method location_unit (line 608) | def location_unit(self, new_loc):
    method orientation (line 615) | def orientation(self):
    method orientation (line 621) | def orientation(self, val):
    method content (line 630) | def content(self):
    method bound_box (line 648) | def bound_box(self):
    method array_box (line 657) | def array_box(self):
    method fill_box (line 670) | def fill_box(self):
    method get_bound_box_of (line 682) | def get_bound_box_of(self, row=0, col=0):
    method move_by (line 690) | def move_by(self, dx=0, dy=0, unit_mode=False):
    method translate_master_box (line 708) | def translate_master_box(self, box):
    method translate_master_location (line 724) | def translate_master_location(self,
    method translate_master_track (line 755) | def translate_master_track(self, layer_id, track_idx):
    method get_port (line 775) | def get_port(self, name='', row=0, col=0):
    method get_pin (line 800) | def get_pin(self, name='', row=0, col=0, layer=-1):
    method get_all_port_pins (line 827) | def get_all_port_pins(self, name='', layer=-1):
    method port_pins_iter (line 855) | def port_pins_iter(self, name='', layer=-1):
    method port_names_iter (line 882) | def port_names_iter(self):
    method has_port (line 893) | def has_port(self, port_name):
    method has_prim_port (line 898) | def has_prim_port(self, port_name):
    method transform (line 903) | def transform(self, loc=(0, 0), orient='R0', unit_mode=False, copy=Fal...
  class Rect (line 919) | class Rect(Arrayable):
    method __init__ (line 942) | def __init__(self, layer, bbox, nx=1, ny=1, spx=0, spy=0, unit_mode=Fa...
    method bbox_array (line 958) | def bbox_array(self):
    method layer (line 970) | def layer(self):
    method layer (line 975) | def layer(self, val):
    method bbox (line 986) | def bbox(self):
    method bbox (line 991) | def bbox(self, val):
    method content (line 1000) | def content(self):
    method move_by (line 1013) | def move_by(self, dx=0, dy=0, unit_mode=False):
    method extend (line 1028) | def extend(self, x=None, y=None):
    method transform (line 1041) | def transform(self, loc=(0, 0), orient='R0', unit_mode=False, copy=Fal...
    method destroy (line 1054) | def destroy(self):
  class Path (line 1061) | class Path(Figure):
    method __init__ (line 1083) | def __init__(self,
    method compress_points (line 1115) | def compress_points(cls, pts_unit):
    method layer (line 1146) | def layer(self):
    method valid (line 1152) | def valid(self):
    method width (line 1158) | def width(self):
    method points (line 1162) | def points(self):
    method points_unit (line 1167) | def points_unit(self):
    method content (line 1172) | def content(self):
    method move_by (line 1183) | def move_by(self, dx=0, dy=0, unit_mode=False):
    method transform (line 1201) | def transform(self, loc=(0, 0), orient='R0', unit_mode=False, copy=Fal...
  class PathCollection (line 1223) | class PathCollection(Figure):
    method __init__ (line 1236) | def __init__(self, resolution, paths, poly_paths = None):
    method move_by (line 1241) | def move_by(self, dx=0, dy=0, unit_mode=False):
    method transform (line 1257) | def transform(self, loc=(0, 0), orient='R0', unit_mode=False, copy=True):
  class TLineBus (line 1270) | class TLineBus(PathCollection):
    method __init__ (line 1293) | def __init__(self, resolution, layer, points, widths, spaces, end_styl...
    method paths_iter (line 1327) | def paths_iter(self):
    method poly_paths_iter (line 1330) | def poly_paths_iter(self):
    method create_paths (line 1333) | def create_paths(self, delta_list, res):
    method create_poly_paths (line 1388) | def create_poly_paths(self, delta_list, res):
  class Polygon (line 1707) | class Polygon(Figure):
    method __init__ (line 1723) | def __init__(self,
    method layer (line 1743) | def layer(self):
    method points (line 1749) | def points(self):
    method points_unit (line 1754) | def points_unit(self):
    method content (line 1759) | def content(self):
    method move_by (line 1767) | def move_by(self, dx=0, dy=0, unit_mode=False):
    method transform (line 1774) | def transform(self, loc=(0, 0), orient='R0', unit_mode=False, copy=Fal...
  class Blockage (line 1796) | class Blockage(Polygon):
    method __init__ (line 1815) | def __init__(self, resolution, block_type, block_layer, points, unit_m...
    method layer (line 1822) | def layer(self):
    method type (line 1827) | def type(self):
    method content (line 1833) | def content(self):
  class Boundary (line 1843) | class Boundary(Polygon):
    method __init__ (line 1860) | def __init__(self, resolution, boundary_type, points, unit_mode=False):
    method type (line 1866) | def type(self):
    method content (line 1872) | def content(self):
  class ViaInfo (line 1881) | class ViaInfo(dict):
    method __init__ (line 1888) | def __init__(self, res, **kwargs):
    method id (line 1898) | def id(self):
    method loc (line 1903) | def loc(self):
    method orient (line 1909) | def orient(self):
    method num_rows (line 1914) | def num_rows(self):
    method num_cols (line 1919) | def num_cols(self):
    method sp_rows (line 1924) | def sp_rows(self):
    method sp_cols (line 1929) | def sp_cols(self):
    method enc1 (line 1934) | def enc1(self):
    method enc2 (line 1940) | def enc2(self):
    method cut_width (line 1946) | def cut_width(self):
    method cut_height (line 1951) | def cut_height(self):
    method arr_nx (line 1956) | def arr_nx(self):
    method arr_ny (line 1961) | def arr_ny(self):
    method arr_spx (line 1966) | def arr_spx(self):
    method arr_spy (line 1971) | def arr_spy(self):
    method move_by (line 1975) | def move_by(self, dx=0, dy=0):
  class Via (line 1992) | class Via(Arrayable):
    method __init__ (line 2026) | def __init__(self, tech, bbox, bot_layer, top_layer, bot_dir,
    method _update (line 2058) | def _update(self):
    method top_box (line 2065) | def top_box(self):
    method bottom_box (line 2071) | def bottom_box(self):
    method bot_layer (line 2077) | def bot_layer(self):
    method top_layer (line 2082) | def top_layer(self):
    method bottom_direction (line 2087) | def bottom_direction(self):
    method bottom_direction (line 2092) | def bottom_direction(self, new_bot_dir):
    method top_direction (line 2099) | def top_direction(self):
    method top_direction (line 2106) | def top_direction(self, new_top_dir):
    method extend (line 2113) | def extend(self):
    method extend (line 2118) | def extend(self, new_val):
    method bbox (line 2122) | def bbox(self):
    method bbox_array (line 2127) | def bbox_array(self):
    method bbox (line 2139) | def bbox(self, new_bbox):
    method content (line 2148) | def content(self):
    method move_by (line 2161) | def move_by(self, dx=0, dy=0, unit_mode=False):
    method transform (line 2179) | def transform(self, loc=(0, 0), orient='R0', unit_mode=False, copy=Fal...
  class PinInfo (line 2196) | class PinInfo(dict):
    method __init__ (line 2202) | def __init__(self, res, **kwargs):
    method net_name (line 2209) | def net_name(self):
    method pin_name (line 2214) | def pin_name(self):
    method label (line 2219) | def label(self):
    method layer (line 2224) | def layer(self):
    method bbox (line 2230) | def bbox(self):
    method make_rect (line 2237) | def make_rect(self):
    method move_by (line 2241) | def move_by(self, dx=0, dy=0):

FILE: bag/layout/routing/base.py
  class TrackID (line 15) | class TrackID(object):
    method __init__ (line 32) | def __init__(self, layer_id, track_idx, width=1, num=1, pitch=0.0):
    method __repr__ (line 43) | def __repr__(self):
    method __str__ (line 60) | def __str__(self):
    method layer_id (line 64) | def layer_id(self):
    method width (line 69) | def width(self):
    method base_index (line 74) | def base_index(self):
    method index_htr (line 81) | def index_htr(self):
    method num (line 86) | def num(self):
    method pitch (line 91) | def pitch(self):
    method pitch_htr (line 98) | def pitch_htr(self):
    method get_immutable_key (line 102) | def get_immutable_key(self):
    method get_bounds (line 105) | def get_bounds(self, grid, unit_mode=False):
    method __iter__ (line 133) | def __iter__(self):
    method sub_tracks_iter (line 143) | def sub_tracks_iter(self, grid):
    method transform (line 174) | def transform(self, grid, loc=(0, 0), orient="R0", unit_mode=False):
  class WireArray (line 202) | class WireArray(object):
    method __init__ (line 219) | def __init__(self, track_id, lower, upper, res=None, unit_mode=False):
    method __repr__ (line 233) | def __repr__(self):
    method __str__ (line 237) | def __str__(self):
    method resolution (line 241) | def resolution(self):
    method lower (line 245) | def lower(self):
    method upper (line 249) | def upper(self):
    method middle (line 253) | def middle(self):
    method lower_unit (line 257) | def lower_unit(self):
    method upper_unit (line 261) | def upper_unit(self):
    method middle_unit (line 265) | def middle_unit(self):
    method track_id (line 269) | def track_id(self):
    method layer_id (line 275) | def layer_id(self):
    method width (line 281) | def width(self):
    method list_to_warr (line 285) | def list_to_warr(cls, warr_list):
    method single_warr_iter (line 316) | def single_warr_iter(cls, warr):
    method get_immutable_key (line 323) | def get_immutable_key(self):
    method to_warr_list (line 327) | def to_warr_list(self):
    method warr_iter (line 330) | def warr_iter(self):
    method get_bbox_array (line 338) | def get_bbox_array(self, grid):
    method wire_iter (line 366) | def wire_iter(self, grid):
    method wire_arr_iter (line 389) | def wire_arr_iter(self, grid):
    method transform (line 427) | def transform(self, grid, loc=(0, 0), orient='R0', unit_mode=False):
  class Port (line 469) | class Port(object):
    method __init__ (line 483) | def __init__(self, term_name, pin_dict, label=''):
    method __iter__ (line 488) | def __iter__(self):
    method get_single_layer (line 496) | def get_single_layer(self):
    method _get_layer (line 503) | def _get_layer(self, layer):
    method net_name (line 511) | def net_name(self):
    method label (line 516) | def label(self):
    method get_pins (line 520) | def get_pins(self, layer=-1):
    method get_bounding_box (line 537) | def get_bounding_box(self, grid, layer=-1):
    method transform (line 562) | def transform(self, grid, loc=(0, 0), orient='R0', unit_mode=False):
  class TrackManager (line 594) | class TrackManager(object):
    method __init__ (line 614) | def __init__(self,
    method grid (line 629) | def grid(self):
    method half_space (line 634) | def half_space(self):
    method get_width (line 638) | def get_width(self, layer_id, track_type):
    method get_space (line 655) | def get_space(self,  # type: TrackManager
    method _get_space_from_tuple (line 708) | def _get_space_from_tuple(cls, layer_id, ntup, sp_dict):
    method _get_space_from_type (line 718) | def _get_space_from_type(cls, layer_id, wtype, sp_dict):
    method get_next_track (line 738) | def get_next_track(self,  # type: TrackManager
    method place_wires (line 778) | def place_wires(self,  # type: TrackManager
    method _get_align_delta (line 823) | def _get_align_delta(cls, tot_ntr, num_used, alignment):
    method align_wires (line 837) | def align_wires(self,  # type: TrackManager
    method spread_wires (line 876) | def spread_wires(self,  # type: TrackManager

FILE: bag/layout/routing/fill.py
  class RectIndex (line 19) | class RectIndex(object):
    method __init__ (line 22) | def __init__(self, resolution, basename=None, overwrite=False):
    method bound_box (line 33) | def bound_box(self):
    method close (line 38) | def close(self):
    method record_box (line 41) | def record_box(self, box, dx, dy):
    method rect_iter (line 50) | def rect_iter(self):
    method intersection_iter (line 56) | def intersection_iter(self, box, dx=0, dy=0):
    method intersection_rect_iter (line 68) | def intersection_rect_iter(self, box):
  class UsedTracks (line 77) | class UsedTracks(object):
    method __init__ (line 81) | def __init__(self, save_file_basename=None, overwrite=False):
    method __iter__ (line 87) | def __iter__(self):
    method get_track_bbox (line 90) | def get_track_bbox(self, layer_id):
    method track_box_iter (line 96) | def track_box_iter(self):
    method record_box (line 101) | def record_box(self, layer_id, box, dx, dy, res):
    method close (line 113) | def close(self):
    method record_rect (line 117) | def record_rect(self, grid, layer_name, box_arr, dx=-1, dy=-1):
    method all_rect_iter (line 165) | def all_rect_iter(self):
    method intersection_rect_iter (line 171) | def intersection_rect_iter(self, layer_id, box):
    method blockage_iter (line 177) | def blockage_iter(self, layer_id, test_box, spx=0, spy=0):
  function fill_symmetric_const_space (line 183) | def fill_symmetric_const_space(area, sp_max, n_min, n_max, offset=0):
  function fill_symmetric_min_density_info (line 264) | def fill_symmetric_min_density_info(area, targ_area, n_min, n_max, sp_min,
  function fill_symmetric_max_density_info (line 349) | def fill_symmetric_max_density_info(area, targ_area, n_min, n_max, sp_min,
  function fill_symmetric_max_density (line 462) | def fill_symmetric_max_density(area,  # type: int
  class InsufficientAreaError (line 519) | class InsufficientAreaError(ValueError):
  class FillTooSmallError (line 523) | class FillTooSmallError(ValueError):
  class NoFillAbutEdgeError (line 527) | class NoFillAbutEdgeError(ValueError):
  class NoFillChoiceError (line 531) | class NoFillChoiceError(ValueError):
  class EmptyRegionError (line 535) | class EmptyRegionError(ValueError):
  function fill_symmetric_max_num_info (line 539) | def fill_symmetric_max_num_info(tot_area, nfill, n_min, n_max, sp_min,
  function _fill_symmetric_info (line 625) | def _fill_symmetric_info(tot_area, num_blk_tot, sp, inc_sp=True, fill_on...
  function _get_min_max_blk_len (line 800) | def _get_min_max_blk_len(fill_info):
  function fill_symmetric_interval (line 808) | def fill_symmetric_interval(tot_area, sp, num_diff_sp, sp_edge, blk0, bl...
  function fill_symmetric_helper (line 894) | def fill_symmetric_helper(tot_area, num_blk_tot, sp, offset=0, inc_sp=Tr...

FILE: bag/layout/routing/grid.py
  class RoutingGrid (line 18) | class RoutingGrid(object):
    method __init__ (line 51) | def __init__(self,  # type: RoutingGrid
    method __contains__ (line 101) | def __contains__(self, layer):
    method get_middle_track (line 107) | def get_middle_track(cls, tr1, tr2, round_up=False):
    method _get_track_offset (line 118) | def _get_track_offset(self, layer_id):
    method get_flip_parity (line 124) | def get_flip_parity(self):
    method get_bot_common_layer (line 129) | def get_bot_common_layer(self, inst_grid, inst_top_layer):
    method get_flip_parity_at (line 160) | def get_flip_parity_at(self,  # type: RoutingGrid
    method set_flip_parity (line 226) | def set_flip_parity(self, fp):
    method tech_info (line 233) | def tech_info(self):
    method resolution (line 239) | def resolution(self):
    method layout_unit (line 245) | def layout_unit(self):
    method top_private_layer (line 251) | def top_private_layer(self):
    method update_block_pitch (line 256) | def update_block_pitch(self):
    method _update_block_pitch_helper (line 272) | def _update_block_pitch_helper(self, lay_list):
    method get_direction (line 290) | def get_direction(self, layer_id):
    method get_track_pitch (line 306) | def get_track_pitch(self, layer_id, unit_mode=False):
    method get_track_width (line 325) | def get_track_width(self, layer_id, width_ntr, unit_mode=False):
    method get_track_width_inverse (line 351) | def get_track_width_inverse(self, layer_id, width, mode=-1, unit_mode=...
    method get_num_tracks (line 398) | def get_num_tracks(self, size, layer_id):
    method get_min_length (line 426) | def get_min_length(self, layer_id, width_ntr, unit_mode=False):
    method get_space (line 457) | def get_space(self, layer_id, width_ntr, same_color=False, unit_mode=F...
    method get_num_space_tracks (line 489) | def get_num_space_tracks(self, layer_id, width_ntr, half_space=False, ...
    method get_line_end_space (line 529) | def get_line_end_space(self, layer_id, width_ntr, unit_mode=False):
    method get_line_end_space_tracks (line 557) | def get_line_end_space_tracks(self, wire_layer, space_layer, width_ntr...
    method get_max_track_width (line 600) | def get_max_track_width(self, layer_id, num_tracks, tot_space, half_en...
    method get_evenly_spaced_tracks (line 640) | def get_evenly_spaced_tracks(num_tracks, tot_space, track_width, half_...
    method get_block_size (line 680) | def get_block_size(self, layer_id, unit_mode=False, include_private=Fa...
    method get_fill_size (line 736) | def get_fill_size(self,  # type: RoutingGrid
    method size_defined (line 790) | def size_defined(self, layer_id):
    method get_size_pitch (line 795) | def get_size_pitch(self, layer_id, unit_mode=False):
    method get_size_tuple (line 827) | def get_size_tuple(self,  # type: RoutingGrid
    method get_size_dimension (line 888) | def get_size_dimension(self,  # type: RoutingGrid
    method convert_size (line 917) | def convert_size(self, size, new_top_layer):
    method get_track_info (line 936) | def get_track_info(self, layer_id, unit_mode=False):
    method get_track_parity (line 959) | def get_track_parity(self, layer_id, tr_idx):
    method get_layer_name (line 983) | def get_layer_name(self, layer_id, tr_idx):
    method get_wire_bounds (line 1007) | def get_wire_bounds(self, layer_id, tr_idx, width=1, unit_mode=False):
    method get_bbox (line 1037) | def get_bbox(self, layer_id, tr_idx, lower, upper, width=1, unit_mode=...
    method get_min_track_width (line 1073) | def get_min_track_width(self, layer_id, idc=0, iac_rms=0, iac_peak=0, ...
    method get_min_track_width_for_via (line 1178) | def get_min_track_width_for_via(self,
    method get_track_index_range (line 1202) | def get_track_index_range(self,  # type: RoutingGrid
    method get_overlap_tracks (line 1287) | def get_overlap_tracks(self,  # type: RoutingGrid
    method get_via_extensions_dim (line 1329) | def get_via_extensions_dim(self,  # type: RoutingGrid
    method get_via_extensions (line 1388) | def get_via_extensions(self, bot_layer_id, bot_width, top_width, unit_...
    method coord_to_track (line 1414) | def coord_to_track(self, layer_id, coord, unit_mode=False):
    method find_next_track (line 1445) | def find_next_track(self, layer_id, coord, tr_width=1, half_track=False,
    method coord_to_nearest_track (line 1482) | def coord_to_nearest_track(self, layer_id, coord, half_track=False, mo...
    method coord_to_nearest_fill_track (line 1549) | def coord_to_nearest_fill_track(self, layer_id, coord, fill_config, mo...
    method transform_track (line 1579) | def transform_track(self,  # type: RoutingGrid
    method track_to_coord (line 1638) | def track_to_coord(self, layer_id, track_idx, unit_mode=False):
    method interval_to_track (line 1662) | def interval_to_track(self,  # type: RoutingGrid
    method copy (line 1710) | def copy(self):
    method ignore_layers_under (line 1735) | def ignore_layers_under(self, layer_id):
    method add_new_layer (line 1749) | def add_new_layer(self, layer_id, tr_space, tr_width, direction,
    method set_track_offset (line 1816) | def set_track_offset(self, layer_id, offset, unit_mode=False):
    method add_width_override (line 1834) | def add_width_override(self, layer_id, width_ntr, tr_width, unit_mode=...

FILE: bag/layout/tech.py
  class TechInfoConfig (line 14) | class TechInfoConfig(TechInfo, metaclass=abc.ABCMeta):
    method __init__ (line 16) | def __init__(self, config, tech_params, mos_entry_name='mos'):
    method get_metal_em_specs (line 26) | def get_metal_em_specs(self, layer_name, w, l=-1, vertical=False, **kw...
    method get_via_em_specs (line 30) | def get_via_em_specs(self, via_name, bm_layer, tm_layer, via_type='squ...
    method get_res_em_specs (line 35) | def get_res_em_specs(self, res_type, w, l=-1, **kwargs):
    method add_cell_boundary (line 39) | def add_cell_boundary(self, template, box):
    method draw_device_blockage (line 44) | def draw_device_blockage(self, template):
    method get_via_arr_enc (line 49) | def get_via_arr_enc(self, vname, vtype, mtype, mw_unit, is_bot):
    method pin_purpose (line 54) | def pin_purpose(self):
    method get_via_types (line 57) | def get_via_types(self, bmtype, tmtype):
    method get_well_layers (line 64) | def get_well_layers(self, sub_type):
    method get_implant_layers (line 68) | def get_implant_layers(self, mos_type, res_type=None):
    method get_threshold_layers (line 77) | def get_threshold_layers(self, mos_type, threshold, res_type=None):
    method get_exclude_layer (line 86) | def get_exclude_layer(self, layer_id):
    method get_dnw_margin_unit (line 91) | def get_dnw_margin_unit(self, dnw_mode):
    method get_dnw_layers (line 95) | def get_dnw_layers(self):
    method get_res_metal_layers (line 99) | def get_res_metal_layers(self, layer_id):
    method get_metal_dummy_layers (line 103) | def get_metal_dummy_layers(self, layer_id):
    method use_flip_parity (line 107) | def use_flip_parity(self):
    method get_layer_name (line 111) | def get_layer_name(self, layer_id):
    method get_layer_id (line 116) | def get_layer_id(self, layer_name):
    method get_layer_type (line 123) | def get_layer_type(self, layer_name):
    method get_idc_scale_factor (line 128) | def get_idc_scale_factor(self, temp, mtype, is_res=False):
    method get_via_name (line 146) | def get_via_name(self, bot_layer_id):
    method get_via_id (line 150) | def get_via_id(self, bot_layer, top_layer):
    method get_via_drc_info (line 154) | def get_via_drc_info(self, vname, vtype, mtype, mw_unit, is_bot):
    method _space_helper (line 214) | def _space_helper(self, config_name, layer_type, width):
    method get_min_space_unit (line 228) | def get_min_space_unit(self, layer_type, w_unit, same_color=False):
    method get_min_line_end_space_unit (line 237) | def get_min_line_end_space_unit(self, layer_type, w_unit):
    method get_min_space (line 240) | def get_min_space(self, layer_type, width, unit_mode=False, same_color...
    method get_min_line_end_space (line 252) | def get_min_line_end_space(self, layer_type, width, unit_mode=False):
    method layer_id_to_type (line 264) | def layer_id_to_type(self, layer_id):
    method get_min_length_unit (line 269) | def get_min_length_unit(self, layer_type, w_unit):
    method get_min_length (line 294) | def get_min_length(self, layer_type, width):
    method get_res_rsquare (line 299) | def get_res_rsquare(self, res_type):
    method get_res_width_bounds (line 302) | def get_res_width_bounds(self, res_type):
    method get_res_length_bounds (line 305) | def get_res_length_bounds(self, res_type):
    method get_res_min_nsquare (line 308) | def get_res_min_nsquare(self, res_type):

FILE: bag/layout/template.py
  class TemplateDB (line 52) | class TemplateDB(MasterDB):
    method __init__ (line 82) | def __init__(self,  # type: TemplateDB
    method create_master_instance (line 133) | def create_master_instance(self, gen_cls, lib_name, params, used_cell_...
    method create_masters_in_db (line 160) | def create_masters_in_db(self, lib_name, content_list, debug=False):
    method grid (line 232) | def grid(self):
    method new_template (line 237) | def new_template(self, lib_name='', temp_name='', params=None, temp_cl...
    method instantiate_layout (line 268) | def instantiate_layout(self, prj, template, top_cell_name=None, debug=...
    method batch_layout (line 287) | def batch_layout(self,
    method save_to_cache (line 317) | def save_to_cache(self, temp_list, dir_name, debug=False):
    method _create_gds (line 333) | def _create_gds(self, lib_name, content_list, debug=False):
    method _add_gds_via (line 454) | def _add_gds_via(self, gds_cell, via, lay_map, via_lay_info, x0, y0):
  class TemplateBase (line 496) | class TemplateBase(DesignMaster, metaclass=abc.ABCMeta):
    method __init__ (line 527) | def __init__(self, temp_db, lib_name, params, used_names, **kwargs):
    method draw_layout (line 564) | def draw_layout(self):
    method populate_params (line 574) | def populate_params(self, table, params_info, default_params, **kwargs):
    method get_master_basename (line 592) | def get_master_basename(self):
    method get_layout_basename (line 603) | def get_layout_basename(self):
    method get_content (line 614) | def get_content(self, lib_name, rename_fun):
    method finalize (line 634) | def finalize(self):
    method get_cache_properties (line 690) | def get_cache_properties(cls):
    method template_db (line 696) | def template_db(self):
    method is_empty (line 703) | def is_empty(self):
    method grid (line 709) | def grid(self):
    method grid (line 715) | def grid(self, new_grid):
    method array_box (line 724) | def array_box(self):
    method array_box (line 730) | def array_box(self, new_array_box):
    method fill_box (line 739) | def fill_box(self):
    method fill_box (line 745) | def fill_box(self, new_box):
    method top_layer (line 754) | def top_layer(self):
    method size (line 764) | def size(self):
    method bound_box (line 770) | def bound_box(self):
    method size (line 783) | def size(self, new_size):
    method used_tracks (line 792) | def used_tracks(self):
    method _update_flip_parity (line 796) | def _update_flip_parity(self):
    method instance_iter (line 808) | def instance_iter(self):
    method blockage_iter (line 811) | def blockage_iter(self, layer_id, test_box, spx=0, spy=0):
    method all_rect_iter (line 819) | def all_rect_iter(self):
    method intersection_rect_iter (line 827) | def intersection_rect_iter(self, layer_id, box):
    method open_interval_iter (line 834) | def open_interval_iter(self,  # type: TemplateBase
    method is_track_available (line 866) | def is_track_available(self,  # type: TemplateBase
    method get_rect_bbox (line 906) | def get_rect_bbox(self, layer):
    method get_track_bbox (line 924) | def get_track_bbox(self, layer_id):
    method track_bbox_iter (line 932) | def track_bbox_iter(self):
    method new_template_with (line 938) | def new_template_with(self, **kwargs):
    method set_size_from_bound_box (line 959) | def set_size_from_bound_box(self, top_layer_id, bbox, round_up=False,
    method set_size_from_array_box (line 986) | def set_size_from_array_box(self, top_layer_id):
    method write_summary_file (line 1011) | def write_summary_file(self, fname, lib_name, cell_name):
    method write_to_disk (line 1050) | def write_to_disk(self, fname, lib_name, cell_name, debug=False):
    method merge_inst_tracks (line 1086) | def merge_inst_tracks(self):
    method get_pin_name (line 1096) | def get_pin_name(self, name):
    method get_port (line 1116) | def get_port(self, name=''):
    method has_port (line 1137) | def has_port(self, port_name):
    method port_names_iter (line 1142) | def port_names_iter(self):
    method get_prim_port (line 1153) | def get_prim_port(self, name=''):
    method has_prim_port (line 1174) | def has_prim_port(self, port_name):
    method prim_port_names_iter (line 1179) | def prim_port_names_iter(self):
    method new_template (line 1190) | def new_template(self, params=None, temp_cls=None, debug=False, **kwar...
    method move_all_by (line 1214) | def move_all_by(self, dx=0.0, dy=0.0, unit_mode=False):
    method add_instance (line 1232) | def add_instance(self,  # type: TemplateBase
    method add_instance_primitive (line 1285) | def add_instance_primitive(self,  # type: TemplateBase
    method add_rect (line 1339) | def add_rect(self,  # type: TemplateBase
    method add_res_metal (line 1379) | def add_res_metal(self, layer_id, bbox, **kwargs):
    method add_path (line 1404) | def add_path(self, path):
    method add_polygon (line 1494) | def add_polygon(self, polygon):
    method add_blockage (line 1511) | def add_blockage(self, blockage):
    method add_cell_boundary (line 1528) | def add_cell_boundary(self, box):
    method add_boundary (line 1541) | def add_boundary(self, boundary):
    method reexport (line 1558) | def reexport(self, port, net_name='', label='', show=True):
    method add_pin_primitive (line 1603) | def add_pin_primitive(self, net_name, layer, bbox, label='', show=True):
    method add_label (line 1643) | def add_label(self, label, layer, bbox):
    method add_pin (line 1660) | def add_pin(self, net_name, wire_arr_list, label='', show=True, edge_m...
    method add_via (line 1723) | def add_via(self,  # type: TemplateBase
    method add_via_primitive (line 1777) | def add_via_primitive(self, via_type,  # type: str
    method add_via_on_grid (line 1856) | def add_via_on_grid(self, bot_layer_id, bot_track, top_track, bot_widt...
    method extend_wires (line 1889) | def extend_wires(self,  # type: TemplateBase
    method add_wires (line 1971) | def add_wires(self,  # type: TemplateBase
    method add_res_metal_warr (line 2021) | def add_res_metal_warr(self,  # type: TemplateBase
    method add_mom_cap (line 2055) | def add_mom_cap(self,  # type: TemplateBase
    method reserve_tracks (line 2340) | def reserve_tracks(self,  # type: TemplateBase
    method connect_wires (line 2380) | def connect_wires(self,  # type: TemplateBase
    method _draw_via_on_track (line 2556) | def _draw_via_on_track(self, wlayer, box_arr, track_id, tl_unit=None,
    method connect_bbox_to_tracks (line 2619) | def connect_bbox_to_tracks(self,  # type: TemplateBase
    method connect_bbox_to_differential_tracks (line 2722) | def connect_bbox_to_differential_tracks(self,  # type: TemplateBase
    method connect_bbox_to_matching_tracks (line 2776) | def connect_bbox_to_matching_tracks(self,  # type: TemplateBase
    method connect_to_tracks (line 2899) | def connect_to_tracks(self,  # type: TemplateBase
    method connect_to_track_wires (line 3048) | def connect_to_track_wires(self,  # type: TemplateBase
    method connect_with_via_stack (line 3098) | def connect_with_via_stack(self,  # type: TemplateBase
    method strap_wires (line 3239) | def strap_wires(self,  # type: TemplateBase
    method _strap_wires_helper (line 3299) | def _strap_wires_helper(self,  # type: TemplateBase
    method connect_differential_tracks (line 3394) | def connect_differential_tracks(self,  # type: TemplateBase
    method connect_differential_wires (line 3449) | def connect_differential_wires(self,  # type: TemplateBase
    method connect_matching_tracks (line 3486) | def connect_matching_tracks(self,  # type: TemplateBase
    method draw_vias_on_intersections (line 3624) | def draw_vias_on_intersections(self, bot_warr_list, top_warr_list):
    method mark_bbox_used (line 3687) | def mark_bbox_used(self, layer_id, bbox):
    method get_available_tracks (line 3694) | def get_available_tracks(self,  # type: TemplateBase
    method do_power_fill (line 3715) | def do_power_fill(self,  # type: TemplateBase
    method do_max_space_fill2 (line 3810) | def do_max_space_fill2(self,  # type: TemplateBase
    method do_max_space_fill (line 3979) | def do_max_space_fill(self,  # type: TemplateBase
    method _fill_poly_bounds (line 4097) | def _fill_poly_bounds(self, poly, layer_id, is_horiz, min_len2, fill_p...
    method _get_flat_poly_iter (line 4144) | def _get_flat_poly_iter(cls, poly):
    method _fill_long_edge_helper (line 4152) | def _fill_long_edge_helper(self, layer_id, grid, tot_geo, long_box, co...
    method _fill_tran_edge_helper (line 4184) | def _fill_tran_edge_helper(self, layer_id, grid, tot_geo, tran_box, tr...
  class CachedTemplate (line 4205) | class CachedTemplate(TemplateBase):
    method __init__ (line 4208) | def __init__(self, temp_db, lib_name, params, used_names, **kwargs):
    method get_params_info (line 4213) | def get_params_info(cls):
    method draw_layout (line 4219) | def draw_layout(self):
  class BlackBoxTemplate (line 4243) | class BlackBoxTemplate(TemplateBase):
    method __init__ (line 4246) | def __init__(self, temp_db, lib_name, params, used_names, **kwargs):
    method sch_params (line 4252) | def sch_params(self):
    method get_params_info (line 4257) | def get_params_info(cls):
    method get_layout_basename (line 4268) | def get_layout_basename(self):
    method draw_layout (line 4271) | def draw_layout(self):
    method _register_pin (line 4302) | def _register_pin(self, lay_id, lay_name, term_name, box, show_pins):

FILE: bag/layout/util.py
  function tuple2_to_int (line 26) | def tuple2_to_int(input_tuple: Tuple[Any, Any]) -> Tuple[int, int]:
  function tuple2_to_float_int (line 35) | def tuple2_to_float_int(input_tuple: Tuple[Any, Any]) -> Tuple[float, int]:
  function transform_point (line 44) | def transform_point(x, y, loc, orient):
  function get_inverse_transform (line 55) | def get_inverse_transform(loc, orient):
  function transform_loc_orient (line 69) | def transform_loc_orient(loc, orient, trans_loc, trans_orient):
  class PortSpec (line 81) | class PortSpec(object):
    method __init__ (line 92) | def __init__(self, ntr, idc):
    method ntr (line 97) | def ntr(self):
    method idc (line 102) | def idc(self):
    method __str__ (line 106) | def __str__(self):
    method __repr__ (line 109) | def __repr__(self):
  class BBox (line 114) | class BBox(object):
    method __init__ (line 134) | def __init__(self, left, bottom, right, top, resolution, unit_mode=Fal...
    method get_invalid_bbox (line 152) | def get_invalid_bbox(cls):
    method left (line 164) | def left(self):
    method left_unit (line 169) | def left_unit(self):
    method right (line 174) | def right(self):
    method right_unit (line 179) | def right_unit(self):
    method bottom (line 184) | def bottom(self):
    method bottom_unit (line 189) | def bottom_unit(self):
    method top (line 194) | def top(self):
    method top_unit (line 199) | def top_unit(self):
    method resolution (line 204) | def resolution(self):
    method width (line 209) | def width(self):
    method width_unit (line 214) | def width_unit(self):
    method height (line 219) | def height(self):
    method height_unit (line 224) | def height_unit(self):
    method xc (line 229) | def xc(self):
    method xc_unit (line 234) | def xc_unit(self):
    method yc (line 239) | def yc(self):
    method yc_unit (line 244) | def yc_unit(self):
    method get_points (line 248) | def get_points(self, unit_mode=False):
    method as_bbox_array (line 273) | def as_bbox_array(self):
    method as_bbox_collection (line 277) | def as_bbox_collection(self):
    method merge (line 281) | def merge(self, bbox):
    method intersect (line 306) | def intersect(self, bbox):
    method overlaps (line 326) | def overlaps(self, bbox):
    method extend (line 335) | def extend(self, x=None, y=None, unit_mode=False):
    method expand (line 367) | def expand(self, dx=0, dy=0, unit_mode=False):
    method transform (line 392) | def transform(self, loc=(0, 0), orient='R0', unit_mode=False):
    method move_by (line 424) | def move_by(self, dx=0, dy=0, unit_mode=False):
    method flip_xy (line 449) | def flip_xy(self):
    method with_interval (line 455) | def with_interval(self, direction, lower, upper, unit_mode=False):
    method get_interval (line 464) | def get_interval(self, direction, unit_mode=False):
    method get_bounds (line 491) | def get_bounds(self, unit_mode=False):
    method is_physical (line 510) | def is_physical(self):
    method is_valid (line 520) | def is_valid(self):
    method get_immutable_key (line 530) | def get_immutable_key(self):
    method __str__ (line 535) | def __str__(self):
    method __repr__ (line 538) | def __repr__(self):
    method __hash__ (line 543) | def __hash__(self):
    method __eq__ (line 546) | def __eq__(self, other):
  class BBoxArray (line 550) | class BBoxArray(object):
    method __init__ (line 571) | def __init__(self, bbox, nx=1, ny=1, spx=0, spy=0, unit_mode=False):
    method __iter__ (line 590) | def __iter__(self):
    method base (line 600) | def base(self):
    method nx (line 606) | def nx(self):
    method ny (line 612) | def ny(self):
    method spx (line 618) | def spx(self):
    method spx_unit (line 624) | def spx_unit(self):
    method spy (line 630) | def spy(self):
    method spy_unit (line 636) | def spy_unit(self):
    method left (line 642) | def left(self):
    method left_unit (line 648) | def left_unit(self):
    method right (line 654) | def right(self):
    method right_unit (line 660) | def right_unit(self):
    method bottom (line 666) | def bottom(self):
    method bottom_unit (line 672) | def bottom_unit(self):
    method top (line 678) | def top(self):
    method top_unit (line 684) | def top_unit(self):
    method xc (line 690) | def xc(self):
    method xc_unit (line 694) | def xc_unit(self):
    method yc (line 699) | def yc(self):
    method yc_unit (line 703) | def yc_unit(self):
    method as_bbox_collection (line 707) | def as_bbox_collection(self):
    method get_bbox (line 712) | def get_bbox(self, idx):
    method get_overall_bbox (line 727) | def get_overall_bbox(self):
    method move_by (line 738) | def move_by(self, dx=0, dy=0, unit_mode=False):
    method transform (line 758) | def transform(self, loc=(0, 0), orient='R0', unit_mode=False):
    method arrayed_copies (line 807) | def arrayed_copies(self, nx=1, ny=1, spx=0, spy=0, unit_mode=False):
    method _array_helper (line 845) | def _array_helper(n1, sp1, n2, sp2):
    method __str__ (line 861) | def __str__(self):
    method __repr__ (line 864) | def __repr__(self):
  class BBoxCollection (line 871) | class BBoxCollection(object):
    method __init__ (line 883) | def __init__(self, box_arr_list):
    method __iter__ (line 886) | def __iter__(self):
    method __reversed__ (line 890) | def __reversed__(self):
    method __len__ (line 893) | def __len__(self):
    method as_bbox_array (line 896) | def as_bbox_array(self):
    method as_bbox (line 914) | def as_bbox(self):
    method get_bounding_box (line 934) | def get_bounding_box(self):
    method transform (line 950) | def transform(self, loc=(0, 0), orient='R0'):
    method __str__ (line 970) | def __str__(self):
    method __repr__ (line 973) | def __repr__(self):
  class Pin (line 977) | class Pin(object):
    method __init__ (line 994) | def __init__(self, pin_name, term_name, layer, bbox):
    method pin_name (line 1004) | def pin_name(self):
    method term_name (line 1009) | def term_name(self):
    method layer (line 1014) | def layer(self):
    method bbox (line 1019) | def bbox(self):
    method __str__ (line 1023) | def __str__(self):
    method __repr__ (line 1026) | def __repr__(self):

FILE: bag/math/__init__.py
  function float_to_si_string (line 18) | def float_to_si_string(num, precision=6):
  function si_string_to_float (line 48) | def si_string_to_float(si_str):
  function gcd (line 68) | def gcd(a, b):
  function lcm (line 89) | def lcm(arr, init=1):

FILE: bag/math/dfun.py
  class DiffFunction (line 12) | class DiffFunction(abc.ABC):
    method __init__ (line 26) | def __init__(self, input_ranges, delta_list=None):
    method input_ranges (line 37) | def input_ranges(self):
    method ndim (line 42) | def ndim(self):
    method __call__ (line 48) | def __call__(self, xi):
    method get_input_range (line 65) | def get_input_range(self, idx):
    method deriv (line 70) | def deriv(self, xi, j):
    method jacobian (line 89) | def jacobian(self, xi):
    method _fd (line 116) | def _fd(self, xi, idx, delta):
    method _fd_jacobian (line 152) | def _fd_jacobian(self, xi, delta_list):
    method transform_input (line 184) | def transform_input(self, amat, bmat):
    method __add__ (line 202) | def __add__(self, other):
    method __radd__ (line 213) | def __radd__(self, other):
    method __sub__ (line 217) | def __sub__(self, other):
    method __rsub__ (line 228) | def __rsub__(self, other):
    method __mul__ (line 239) | def __mul__(self, other):
    method __rmul__ (line 250) | def __rmul__(self, other):
    method __pow__ (line 254) | def __pow__(self, other):
    method __div__ (line 263) | def __div__(self, other):
    method __truediv__ (line 274) | def __truediv__(self, other):
    method __rdiv__ (line 278) | def __rdiv__(self, other):
    method __rtruediv__ (line 289) | def __rtruediv__(self, other):
    method __neg__ (line 293) | def __neg__(self):
  class InLinTransformFunction (line 298) | class InLinTransformFunction(DiffFunction):
    method __init__ (line 312) | def __init__(self, f1, amat, bmat):
    method _get_arg (line 325) | def _get_arg(self, xi):
    method __call__ (line 335) | def __call__(self, xi):
    method deriv (line 342) | def deriv(self, xi, j):
    method jacobian (line 346) | def jacobian(self, xi):
  class ScaleAddFunction (line 353) | class ScaleAddFunction(DiffFunction):
    method __init__ (line 365) | def __init__(self, f1, adder, scaler):
    method __call__ (line 372) | def __call__(self, xi):
    method deriv (line 375) | def deriv(self, xi, j):
    method jacobian (line 378) | def jacobian(self, xi):
  function _intersection (line 382) | def _intersection(*args):
  class SumDiffFunction (line 402) | class SumDiffFunction(DiffFunction):
    method __init__ (line 414) | def __init__(self, f1, f2, f2_sgn=1.0):
    method __call__ (line 424) | def __call__(self, xi):
    method deriv (line 427) | def deriv(self, xi, j):
    method jacobian (line 430) | def jacobian(self, xi):
  class ProdFunction (line 434) | class ProdFunction(DiffFunction):
    method __init__ (line 444) | def __init__(self, f1, f2):
    method __call__ (line 453) | def __call__(self, xi):
    method deriv (line 456) | def deriv(self, xi, j):
    method jacobian (line 459) | def jacobian(self, xi):
  class DivFunction (line 467) | class DivFunction(DiffFunction):
    method __init__ (line 477) | def __init__(self, f1, f2):
    method __call__ (line 486) | def __call__(self, xi):
    method deriv (line 489) | def deriv(self, xi, j):
    method jacobian (line 493) | def jacobian(self, xi):
  class PwrFunction (line 502) | class PwrFunction(DiffFunction):
    method __init__ (line 514) | def __init__(self, f, pwr, scale=1.0):
    method __call__ (line 521) | def __call__(self, xi):
    method deriv (line 524) | def deriv(self, xi, j):
    method jacobian (line 527) | def jacobian(self, xi):
  class VectorDiffFunction (line 533) | class VectorDiffFunction(object):
    method __init__ (line 542) | def __init__(self, fun_list):
    method in_dim (line 559) | def in_dim(self):
    method out_dim (line 565) | def out_dim(self):
    method get_input_range (line 570) | def get_input_range(self, idx):
    method __call__ (line 575) | def __call__(self, xi):
    method jacobian (line 595) | def jacobian(self, xi):
    method deriv (line 615) | def deriv(self, xi, i, j):

FILE: bag/math/interpolate.py
  function _scales_to_points (line 18) | def _scales_to_points(scale_list, values, delta=1e-4):
  function interpolate_grid (line 42) | def interpolate_grid(scale_list, values, method='spline',
  class LinearInterpolator (line 91) | class LinearInterpolator(DiffFunction):
    method __init__ (line 109) | def __init__(self, points, values, delta_list, extrapolate=False):
    method get_input_points (line 119) | def get_input_points(self, idx):
    method __call__ (line 124) | def __call__(self, xi):
    method integrate (line 142) | def integrate(self, xstart, xstop, axis=-1, logx=False, logy=False, ra...
  class Interpolator1D (line 268) | class Interpolator1D(DiffFunction):
    method __init__ (line 285) | def __init__(self, scale_list, values, method='spline', extrapolate=Fa...
    method __call__ (line 308) | def __call__(self, xi):
    method deriv (line 326) | def deriv(self, xi, idx):
  class Spline2D (line 350) | class Spline2D(DiffFunction):
    method __init__ (line 365) | def __init__(self, scale_list, values, extrapolate=False):
    method _get_xy (line 386) | def _get_xy(self, xi):
    method __call__ (line 402) | def __call__(self, xi):
    method deriv (line 418) | def deriv(self, xi, idx):
  class MapCoordinateSpline (line 443) | class MapCoordinateSpline(DiffFunction):
    method __init__ (line 472) | def __init__(self, scale_list, values, extrapolate=False, num_extrapol...
    method _normalize_inputs (line 502) | def _normalize_inputs(self, xi):
    method __call__ (line 519) | def __call__(self, xi):

FILE: bag/mdao/components.py
  class VecFunComponent (line 10) | class VecFunComponent(omdao.Component):
    method __init__ (line 33) | def __init__(self, output_name, fun_list, params,
    method __call__ (line 62) | def __call__(self, **kwargs):
    method _get_inputs (line 79) | def _get_inputs(self, params):
    method solve_nonlinear (line 97) | def solve_nonlinear(self, params, unknowns, resids=None):
    method linearize (line 119) | def linearize(self, params, unknowns=None, resids=None):

FILE: bag/mdao/core.py
  class GroupBuilder (line 14) | class GroupBuilder(object):
    method __init__ (line 23) | def __init__(self):
    method _add_node (line 27) | def _add_node(self, name, ndim, **kwargs):
    method _add_edge (line 32) | def _add_edge(self, parent, child):
    method get_inputs (line 40) | def get_inputs(self):
    method get_variables (line 50) | def get_variables(self):
    method get_variable_info (line 60) | def get_variable_info(self, name):
    method add_fun (line 80) | def add_fun(self, var_name, fun_list, params, param_ranges, vector_par...
    method add_var (line 139) | def add_var(self, variable, vmin, vmax, ndim=1):
    method set_input_limit (line 157) | def set_input_limit(self, var, equals=None, lower=None, upper=None):
    method add_expr (line 185) | def add_expr(self, eqn, ndim):
    method build (line 217) | def build(self, debug=False):

FILE: bag/simulation/core.py
  class TestbenchManager (line 23) | class TestbenchManager(object, metaclass=abc.ABCMeta):
    method __init__ (line 43) | def __init__(self,
    method setup_testbench (line 60) | def setup_testbench(self, tb):
    method setup_and_simulate (line 74) | async def setup_and_simulate(self, prj: BagProject,
    method record_array (line 99) | def record_array(cls, output_dict, data_dict, arr, arr_name, sweep_par...
    method _create_tb_schematic (line 132) | def _create_tb_schematic(self, prj, sch_params):
  class MeasurementManager (line 157) | class MeasurementManager(object, metaclass=abc.ABCMeta):
    method __init__ (line 184) | def __init__(self,  # type: MeasurementManager
    method get_initial_state (line 205) | def get_initial_state(self):
    method get_testbench_info (line 211) | def get_testbench_info(self,  # type: MeasurementManager
    method process_output (line 248) | def process_output(self, state, data, tb_manager):
    method get_testbench_name (line 272) | def get_testbench_name(self, tb_type):
    method async_measure_performance (line 277) | async def async_measure_performance(self,
    method get_state_output (line 339) | def get_state_output(self, state):
    method get_testbench_specs (line 345) | def get_testbench_specs(self, tb_type):
    method get_default_tb_sch_params (line 350) | def get_default_tb_sch_params(self, tb_type):
  class DesignManager (line 380) | class DesignManager(object):
    method __init__ (line 395) | def __init__(self, prj, spec_file):
    method load_state (line 412) | def load_state(cls, prj, root_dir):
    method get_measurement_name (line 418) | def get_measurement_name(cls, dsn_name, meas_type):
    method get_wrapper_name (line 437) | def get_wrapper_name(cls, dut_name, wrapper_name):
    method specs (line 443) | def specs(self):
    method swp_var_list (line 449) | def swp_var_list(self):
    method extract_design (line 453) | async def extract_design(self, lib_name: str, dsn_name: str,
    method verify_design (line 479) | async def verify_design(self, lib_name: str, dsn_name: str,
    method main_task (line 530) | async def main_task(self, lib_name: str, dsn_name: str,
    method characterize_designs (line 541) | def characterize_designs(self, generate=True, measure=True, load_from_...
    method get_result (line 576) | def get_result(self, dsn_name):
    method test_layout (line 595) | def test_layout(self, gen_sch=True):
    method create_designs (line 614) | def create_designs(self, create_layout):
    method get_swp_var_values (line 645) | def get_swp_var_values(self, var):
    method get_combinations_iter (line 661) | def get_combinations_iter(self):
    method get_dsn_name_iter (line 674) | def get_dsn_name_iter(self):
    method get_measurement_directory (line 685) | def get_measurement_directory(self, dsn_name, meas_type):
    method make_tdb (line 689) | def make_tdb(self):
    method get_layout_params (line 713) | def get_layout_params(self, val_list):
    method get_schematic_params (line 722) | def get_schematic_params(self, val_list):
    method create_dut_schematics (line 731) | def create_dut_schematics(self, sch_params_list, cell_name_list, gen_w...
    method create_dut_layouts (line 759) | def create_dut_layouts(self, lay_params_list, cell_name_list, temp_db):
    method get_design_name (line 779) | def get_design_name(self, combo_list):

FILE: bag/simulation/core_v2.py
  class TestbenchManager (line 21) | class TestbenchManager(abc.ABC):
    method __init__ (line 32) | def __init__(self, work_dir: Path) -> None:
    method work_dir (line 38) | def work_dir(self) -> Path:
    method specs (line 42) | def specs(self):
    method sim_vars (line 46) | def sim_vars(self):
    method pre_setup (line 50) | def pre_setup(self, tb_params: Optional[Dict[str, Any]]) -> Optional[D...
    method setup (line 66) | def setup(self, bprj, impl_lib, impl_cell, sim_view_list, env_list,
    method setup_and_simulate (line 140) | async def setup_and_simulate(self, bprj, impl_lib, impl_cell, sim_view...
    method simulate (line 155) | def simulate(self, bprj, impl_lib, impl_cell, sim_view_list, env_list,...
    method load_results (line 166) | def load_results(self, impl_cell, tb_dict):
  class MeasurementManager (line 179) | class MeasurementManager(abc.ABC):
    method __init__ (line 181) | def __init__(self, work_dir: Path, mm_specs: Dict[str, Any]) -> None:
    method specs (line 197) | def specs(self):
    method work_dir (line 201) | def work_dir(self):
    method _prepare_tb_specs (line 204) | def _prepare_tb_specs(self) -> None:
    method _prepare_tbm_dict (line 213) | def _prepare_tbm_dict(self, impl_cell, tbm_dict, extract):
    method _wrapper_exists (line 233) | def _wrapper_exists(self, wrapper: ImmutableType) -> bool:
    method run_tb (line 237) | def run_tb(self, bprj, impl_lib, impl_cell, tb_name, tbm_dict=None, ex...
    method run_flow (line 268) | def run_flow(self, bprj: BagProject, impl_lib: str, impl_cell: str,
    method measure (line 296) | def measure(self, bprj: BagProject, impl_lib: str, impl_cell: str, loa...

FILE: bag/tech/core.py
  function _equal (line 23) | def _equal(a, b, rtol, atol):
  function _equal_list (line 34) | def _equal_list(a, b, rtol, atol):
  function _index_in_list (line 44) | def _index_in_list(item_list, item, rtol, atol):
  function _in_list (line 52) | def _in_list(item_list, item, rtol, atol):
  class CircuitCharacterization (line 57) | class CircuitCharacterization(SimulationManager, metaclass=abc.ABCMeta):
    method __init__ (line 88) | def __init__(self, prj, spec_file, tb_type, compression='gzip'):
    method record_results (line 95) | def record_results(self, data, tb_type, val_list):
    method get_sim_results (line 129) | def get_sim_results(self, tb_type, val_list):
    method _get_env_result (line 134) | def _get_env_result(self, sim_results, env):
  class CharDB (line 179) | class CharDB(abc.ABC):
    method __init__ (line 220) | def __init__(self,  # type: CharDB
    method _convert_hdf5_array (line 327) | def _convert_hdf5_array(arr):
    method _load_sim_data (line 334) | def _load_sim_data(self,  # type: CharDB
    method __getitem__ (line 453) | def __getitem__(self, param):
    method __setitem__ (line 469) | def __setitem__(self, key, value):
    method get_config (line 498) | def get_config(self, name):
    method set_config (line 514) | def set_config(self, name, value):
    method env_list (line 530) | def env_list(self):
    method env_list (line 536) | def env_list(self, new_env_list):
    method get_sim_file (line 542) | def get_sim_file(cls, root_dir, constants):
    method get_cache_file (line 561) | def get_cache_file(cls, root_dir, constants):
    method post_process_data (line 580) | def post_process_data(cls, sim_data, sweep_params, sweep_values, const...
    method derived_parameters (line 603) | def derived_parameters(cls):
    method compute_derived_parameters (line 609) | def compute_derived_parameters(cls, fdict):
    method _get_function_index (line 625) | def _get_function_index(self, **kwargs):
    method _get_function_helper (line 656) | def _get_function_helper(self, name, fidx_list):
    method get_function (line 700) | def get_function(self, name, env='', **kwargs):
    method get_fun_sweep_params (line 737) | def get_fun_sweep_params(self):
    method _get_fun_arg (line 750) | def _get_fun_arg(self, **kwargs):
    method query (line 762) | def query(self, **kwargs):
    method minimize (line 791) | def minimize(self,  # type: CharDB

FILE: bag/tech/mos.py
  class MosCharDB (line 14) | class MosCharDB(CharDB):
    method __init__ (line 50) | def __init__(self, root_dir, mos_type, discrete_params, env_list,
    method get_sim_file (line 58) | def get_sim_file(cls, root_dir, constants):
    method get_cache_file (line 76) | def get_cache_file(cls, root_dir, constants):
    method post_process_data (line 94) | def post_process_data(cls, sim_data, sweep_params, sweep_values, const...
    method derived_parameters (line 145) | def derived_parameters(cls):
    method compute_derived_parameters (line 150) | def compute_derived_parameters(cls, fdict):
  class MosCharGDDB (line 175) | class MosCharGDDB(CharDB):
    method __init__ (line 211) | def __init__(self, root_dir, mos_type, discrete_params, env_list,
    method get_sim_file (line 219) | def get_sim_file(cls, root_dir, constants):
    method get_cache_file (line 237) | def get_cache_file(cls, root_dir, constants):
    method post_process_data (line 255) | def post_process_data(cls, sim_data, sweep_params, sweep_values, const...
    method derived_parameters (line 298) | def derived_parameters(cls):
    method compute_derived_parameters (line 303) | def compute_derived_parameters(cls, fdict):

FILE: bag/util/cache.py
  function _get_unique_name (line 20) | def _get_unique_name(basename, *args):
  class ClassImporter (line 72) | class ClassImporter(object):
    method __init__ (line 82) | def __init__(self, lib_defs):
    method append_library (line 105) | def append_library(self, lib_name, lib_path):
    method get_library_path (line 120) | def get_library_path(self, lib_name):
    method get_class (line 135) | def get_class(self, lib_name, cell_name):
  class DesignMaster (line 163) | class DesignMaster(abc.ABC):
    method __init__ (line 181) | def __init__(self, master_db, lib_name, params, used_names, **kwargs):
    method update_master_info (line 206) | def update_master_info(self):
    method populate_params (line 210) | def populate_params(self, table, params_info, default_params, **kwargs):
    method to_immutable_id (line 228) | def to_immutable_id(cls, val):
    method get_params_info (line 250) | def get_params_info(cls):
    method get_default_param_values (line 262) | def get_default_param_values(cls):
    method get_master_basename (line 279) | def get_master_basename(self):
    method get_content (line 291) | def get_content(self, lib_name, rename_fun):
    method master_db (line 310) | def master_db(self):
    method lib_name (line 316) | def lib_name(self):
    method cell_name (line 322) | def cell_name(self):
    method key (line 328) | def key(self):
    method finalized (line 334) | def finalized(self):
    method prelim_key (line 340) | def prelim_key(self):
    method _get_qualified_name (line 345) | def _get_qualified_name(self):
    method finalize (line 354) | def finalize(self):
    method compute_unique_key (line 360) | def compute_unique_key(self):
  class MasterDB (line 375) | class MasterDB(abc.ABC):
    method __init__ (line 393) | def __init__(self, lib_name, lib_defs='', name_prefix='', name_suffix=...
    method clear (line 406) | def clear(self):
    method create_master_instance (line 413) | def create_master_instance(self, gen_cls, lib_name, params, used_cell_...
    method create_masters_in_db (line 440) | def create_masters_in_db(self, lib_name, content_list, debug=False):
    method lib_name (line 456) | def lib_name(self):
    method cell_prefix (line 462) | def cell_prefix(self):
    method cell_prefix (line 468) | def cell_prefix(self, new_val):
    method cell_suffix (line 474) | def cell_suffix(self):
    method cell_suffix (line 480) | def cell_suffix(self, new_val):
    method used_cell_names (line 486) | def used_cell_names(self):
    method format_cell_name (line 490) | def format_cell_name(self, cell_name):
    method append_library (line 507) | def append_library(self, lib_name, lib_path):
    method get_library_path (line 523) | def get_library_path(self, lib_name):
    method get_generator_class (line 543) | def get_generator_class(self, lib_name, cell_name):
    method new_master (line 564) | def new_master(self,  # type: MasterDB
    method register_master (line 649) | def register_master(self, key, master):
    method instantiate_masters (line 653) | def instantiate_masters(self,
    method _instantiate_master_helper (line 733) | def _instantiate_master_helper(self, info_dict, master):

FILE: bag/util/immutable.py
  function combine_hash (line 17) | def combine_hash(a: int, b: int) -> int:
  class ImmutableList (line 36) | class ImmutableList(Hashable, Sequence, Generic[T]):
    method __init__ (line 39) | def __init__(self, values: Optional[Sequence[T]] = None) -> None:
    method sequence_equal (line 53) | def sequence_equal(cls, a: Sequence[T], b: Sequence[T]) -> bool:
    method __repr__ (line 61) | def __repr__(self) -> str:
    method __eq__ (line 64) | def __eq__(self, other: Any) -> bool:
    method __hash__ (line 68) | def __hash__(self) -> int:
    method __bool__ (line 71) | def __bool__(self) -> bool:
    method __len__ (line 74) | def __len__(self) -> int:
    method __iter__ (line 77) | def __iter__(self) -> Iterable[T]:
    method __getitem__ (line 81) | def __getitem__(self, idx: int) -> T: ...
    method __getitem__ (line 83) | def __getitem__(self, idx: slice) -> ImmutableList[T]: ...
    method __getitem__ (line 85) | def __getitem__(self, idx) -> T:
    method __contains__ (line 90) | def __contains__(self, val: Any) -> bool:
  class ImmutableSortedDict (line 94) | class ImmutableSortedDict(Hashable, Mapping, Generic[T, U]):
    method __init__ (line 97) | def __init__(self,
    method __repr__ (line 113) | def __repr__(self) -> str:
    method __eq__ (line 116) | def __eq__(self, other: Any) -> bool:
    method __hash__ (line 122) | def __hash__(self) -> int:
    method __bool__ (line 125) | def __bool__(self) -> bool:
    method __len__ (line 128) | def __len__(self) -> int:
    method __iter__ (line 131) | def __iter__(self) -> Iterable[T]:
    method __contains__ (line 134) | def __contains__(self, item: Any) -> bool:
    method __getitem__ (line 138) | def __getitem__(self, item: T) -> U:
    method get (line 144) | def get(self, item: T, default: Optional[U] = None) -> Optional[U]:
    method keys (line 150) | def keys(self) -> Iterable[T]:
    method values (line 153) | def values(self) -> Iterable[U]:
    method items (line 156) | def items(self) -> Iterable[Tuple[T, U]]:
    method copy (line 159) | def copy(self, append: Optional[Dict[T, Any]] = None) -> ImmutableSort...
    method to_dict (line 167) | def to_dict(self) -> Dict[T, U]:
  function to_immutable (line 174) | def to_immutable(obj: Any) -> ImmutableType:

FILE: bag/util/interval.py
  class IntervalSet (line 11) | class IntervalSet(object):
    method __init__ (line 24) | def __init__(self, intv_list=None, val_list=None):
    method __contains__ (line 39) | def __contains__(self, key):
    method __getitem__ (line 56) | def __getitem__(self, intv):
    method __setitem__ (line 77) | def __setitem__(self, intv, value):
    method __iter__ (line 98) | def __iter__(self):
    method __len__ (line 109) | def __len__(self):
    method get_start (line 120) | def get_start(self):
    method get_end (line 131) | def get_end(self):
    method get_interval (line 142) | def get_interval(self, idx):
    method copy (line 153) | def copy(self):
    method _get_first_overlap_idx (line 165) | def _get_first_overlap_idx(self, intv, abut=False):
    method _get_last_overlap_idx (line 206) | def _get_last_overlap_idx(self, intv, abut=False):
    method has_overlap (line 239) | def has_overlap(self, intv):
    method has_single_cover (line 255) | def has_single_cover(self, intv):
    method remove (line 263) | def remove(self, intv):
    method get_intersection (line 287) | def get_intersection(self, other):
    method get_complement (line 323) | def get_complement(self, total_intv):
    method complement_iter (line 342) | def complement_iter(self, total_intv):
    method remove_all_overlaps (line 360) | def remove_all_overlaps(self, intv):
    method add (line 376) | def add(self, intv, val=None, merge=False, abut=False):
    method subtract (line 422) | def subtract(self, intv):
    method items (line 459) | def items(self):
    method intervals (line 474) | def intervals(self):
    method values (line 487) | def values(self):
    method overlap_items (line 500) | def overlap_items(self, intv):
    method overlap_intervals (line 522) | def overlap_intervals(self, intv):
    method overlap_values (line 542) | def overlap_values(self, intv):
    method get_first_overlap_item (line 562) | def get_first_overlap_item(self, intv):
    method transform (line 570) | def transform(self, scale=1, shift=0):

FILE: bag/util/parse.py
  class ExprVarScanner (line 9) | class ExprVarScanner(ast.NodeVisitor):
    method __init__ (line 15) | def __init__(self):
    method visit_Name (line 19) | def visit_Name(self, node):
    method visit_Call (line 23) | def visit_Call(self, node):
    method visit_Attribute (line 30) | def visit_Attribute(self, node):
  function get_variables (line 35) | def get_variables(expr):

FILE: bag/util/search.py
  class BinaryIterator (line 13) | class BinaryIterator(object):
    method __init__ (line 29) | def __init__(self, low, high, step=1):
    method set_current (line 58) | def set_current(self, val):
    method has_next (line 65) | def has_next(self):
    method get_next (line 70) | def get_next(self):
    method up (line 75) | def up(self):
    method down (line 88) | def down(self):
    method save (line 94) | def save(self):
    method save_info (line 99) | def save_info(self, info):
    method get_last_save (line 105) | def get_last_save(self):
    method get_last_save_info (line 112) | def get_last_save_info(self):
  class FloatBinaryIterator (line 118) | class FloatBinaryIterator(object):
    method __init__ (line 138) | def __init__(self, low, high, tol=1.0, search_step=1.0):
    method has_next (line 157) | def has_next(self):
    method get_next (line 162) | def get_next(self):
    method up (line 167) | def up(self):
    method down (line 180) | def down(self):
    method save (line 186) | def save(self):
    method save_info (line 191) | def save_info(self, info):
    method get_last_save (line 197) | def get_last_save(self):
    method get_last_save_info (line 204) | def get_last_save_info(self):
  function minimize_cost_binary (line 210) | def minimize_cost_binary(f, vmin, start=0, stop=None, step=1, save=None,...
  function minimize_cost_golden (line 262) | def minimize_cost_golden(f, vmin, offset=0, step=1, maxiter=1000):
  function minimize_cost_binary_float (line 364) | def minimize_cost_binary_float(f, vmin, start, stop, tol=1e-8, save=None...
  function minimize_cost_golden_float (line 416) | def minimize_cost_golden_float(f, vmin, start, stop, tol=1e-8, maxiter=1...

FILE: bag/verification/__init__.py
  function make_checker (line 15) | def make_checker(checker_cls, tmp_dir, **kwargs):

FILE: bag/verification/base.py
  class Checker (line 16) | class Checker(abc.ABC):
    method __init__ (line 24) | def __init__(self, tmp_dir):
    method get_rcx_netlists (line 30) | def get_rcx_netlists(self, lib_name, cell_name):
    method async_run_lvs (line 49) | async def async_run_lvs(self, lib_name, cell_name, sch_view='schematic',
    method async_run_rcx (line 82) | async def async_run_rcx(self, lib_name, cell_name, sch_view='schematic',
    method async_export_layout (line 115) | async def async_export_layout(self, lib_name, cell_name, out_file,
    method async_export_schematic (line 141) | async def async_export_schematic(self, lib_name, cell_name, out_file,
    method render_file_template (line 166) | def render_file_template(self, temp_name, params):
    method render_string_template (line 172) | def render_string_template(self, content, params):
  class SubProcessChecker (line 179) | class SubProcessChecker(Checker, abc.ABC):
    method __init__ (line 192) | def __init__(self, tmp_dir, max_workers, cancel_timeout):
    method setup_lvs_flow (line 198) | def setup_lvs_flow(self, lib_name, cell_name, sch_view='schematic',
    method setup_rcx_flow (line 242) | def setup_rcx_flow(self, lib_name, cell_name, sch_view='schematic',
    method setup_export_layout (line 285) | def setup_export_layout(self, lib_name, cell_name, out_file, view_name...
    method setup_export_schematic (line 316) | def setup_export_schematic(self, lib_name, cell_name, out_file,
    method async_run_lvs (line 347) | async def async_run_lvs(self, lib_name: str, cell_name: str,
    method async_run_rcx (line 357) | async def async_run_rcx(self, lib_name: str, cell_name: str,
    method async_export_layout (line 366) | async def async_export_layout(self, lib_name: str, cell_name: str,
    method async_export_schematic (line 373) | async def async_export_schematic(self, lib_name: str, cell_name: str,

FILE: bag/verification/calibre.py
  function _all_pass (line 20) | def _all_pass(retcode, log_file):
  function lvs_passed (line 25) | def lvs_passed(retcode, log_file):
  function query_passed (line 54) | def query_passed(retcode, log_file):
  class Calibre (line 82) | class Calibre(VirtuosoChecker):
    method __init__ (line 106) | def __init__(self, tmp_dir, lvs_run_dir, lvs_runset, rcx_run_dir, rcx_...
    method get_rcx_netlists (line 131) | def get_rcx_netlists(self, lib_name, cell_name):
    method setup_lvs_flow (line 156) | def setup_lvs_flow(self, lib_name, cell_name, sch_view='schematic', la...
    method setup_rcx_flow (line 208) | def setup_rcx_flow(self, lib_name, cell_name, sch_view='schematic', la...
    method _get_lay_sch_files (line 354) | def _get_lay_sch_files(cls, run_dir):
    method modify_lvs_runset (line 359) | def modify_lvs_runset(self, run_dir, lib_name, cell_name, lay_view, gd...
    method modify_pex_runset (line 415) | def modify_pex_runset(self, run_dir, lib_name, cell_name, lay_view, gd...
    method modify_xact_rules (line 478) | def modify_xact_rules(self, run_dir, cell_name, gds_file, netlist, xac...
    method modify_starrc_cmd (line 520) | def modify_starrc_cmd(self, run_dir, lib_name, cell_name, starrc_param...
    method modify_qrc_cmd (line 567) | def modify_qrc_cmd(self, run_dir, cell_name, qrc_params, sch_file):

FILE: bag/verification/icv.py
  function _all_pass (line 19) | def _all_pass(retcode, log_file):
  function lvs_passed (line 24) | def lvs_passed(retcode, log_file):
  class ICV (line 62) | class ICV(VirtuosoChecker):
    method __init__ (line 84) | def __init__(self, tmp_dir, lvs_run_dir, lvs_runset, rcx_run_dir, rcx_...
    method get_rcx_netlists (line 111) | def get_rcx_netlists(self, lib_name, cell_name):
    method setup_lvs_flow (line 133) | def setup_lvs_flow(self, lib_name, cell_name, sch_view='schematic', la...
    method setup_rcx_flow (line 182) | def setup_rcx_flow(self, lib_name, cell_name, sch_view='schematic', la...
    method _get_lay_sch_files (line 284) | def _get_lay_sch_files(cls, run_dir):
    method modify_starrc_cmd (line 289) | def modify_starrc_cmd(self, run_dir, lib_name, cell_name, starrc_param...

FILE: bag/verification/pvs.py
  function _all_pass (line 20) | def _all_pass(retcode, log_file):
  function lvs_passed (line 25) | def lvs_passed(retcode, log_file):
  function rcx_passed (line 54) | def rcx_passed(retcode, log_file):
  class PVS (line 84) | class PVS(VirtuosoChecker):
    method __init__ (line 104) | def __init__(self, tmp_dir, lvs_run_dir, lvs_runset, lvs_rule_file, rc...
    method get_rcx_netlists (line 121) | def get_rcx_netlists(self, lib_name, cell_name):
    method setup_lvs_flow (line 126) | def setup_lvs_flow(self, lib_name, cell_name, sch_view='schematic', la...
    method setup_rcx_flow (line 185) | def setup_rcx_flow(self, lib_name, cell_name, sch_view='schematic', la...
    method modify_lvs_runset (line 215) | def modify_lvs_runset(self, run_dir, cell_name, lvs_params):
    method modify_rcx_runset (line 260) | def modify_rcx_runset(self, run_dir, lib_name, cell_name, lay_view, rc...

FILE: bag/verification/virtuoso.py
  class VirtuosoChecker (line 18) | class VirtuosoChecker(SubProcessChecker, ABC):
    method __init__ (line 35) | def __init__(self, tmp_dir, max_workers, cancel_timeout, source_added_...
    method setup_export_layout (line 40) | def setup_export_layout(self, lib_name, cell_name, out_file, view_name...
    method setup_export_schematic (line 69) | def setup_export_schematic(self, lib_name, cell_name, out_file, view_n...

FILE: bag/virtuoso.py
  function run_skill_server (line 16) | def run_skill_server(args):
  function parse_command_line_arguments (line 90) | def parse_command_line_arguments():

FILE: run_scripts/clean_cds_lib.py
  function arg_parse (line 17) | def arg_parse() -> Namespace:
  function run_main (line 28) | def run_main(args: Namespace):

FILE: run_scripts/gen_cell.py
  function parse_args (line 14) | def parse_args() -> Namespace:
  function run_main (line 44) | def run_main(prj: BagProject, args: Namespace):

FILE: run_scripts/generate_verilog.py
  function run_main (line 8) | def run_main():

FILE: run_scripts/meas_cell.py
  function parse_args (line 15) | def parse_args() -> Namespace:
  function run_main (line 40) | def run_main(prj: BagProject, args: Namespace):

FILE: run_scripts/setup_submodules.py
  function write_to_file (line 26) | def write_to_file(fname, lines):
  function setup_python_path (line 32) | def setup_python_path(module_list):
  function get_sch_libraries (line 48) | def get_sch_libraries(mod_name, mod_info):
  function setup_libs_def (line 56) | def setup_libs_def(module_list):
  function setup_cds_lib (line 67) | def setup_cds_lib(module_list):
  function run_command (line 77) | def run_command(cmd):
  function add_git_submodule (line 107) | def add_git_submodule(module_name, url):
  function add_git_file (line 115) | def add_git_file(fname):
  function link_submodule (line 119) | def link_submodule(repo_path, module_name):
  function setup_git_submodules (line 131) | def setup_git_submodules(module_list):
  function setup_submodule_links (line 138) | def setup_submodule_links(module_list, repo_path):
  function run_main (line 144) | def run_main():

FILE: run_scripts/sim_cell.py
  function parse_args (line 15) | def parse_args() -> Namespace:
  function run_main (line 40) | def run_main(prj: BagProject, args: Namespace):

FILE: tests/layout/routing/test_fill.py
  function check_disjoint_union (line 8) | def check_disjoint_union(outer_list, inner_list, start, stop):
  function check_symmetric (line 33) | def check_symmetric(intv_list, start, stop):
  function check_props (line 40) | def check_props(fill_list, space_list, num_diff_sp1, num_diff_sp2, n, to...
  function test_fill_symmetric_non_cyclic (line 86) | def test_fill_symmetric_non_cyclic():
  function test_fill_symmetric_cyclic_edge_fill (line 125) | def test_fill_symmetric_cyclic_edge_fill():
  function test_fill_symmetric_cyclic_edge_space (line 166) | def test_fill_symmetric_cyclic_edge_space():
Condensed preview — 162 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (1,518K chars).
[
  {
    "path": ".gitignore",
    "chars": 57,
    "preview": "*~\n*.pyc\n.idea\nbuild\ndist\nbag.egg-info\n__pycache__\n*.swp\n"
  },
  {
    "path": ".gitmodules",
    "chars": 90,
    "preview": "[submodule \"cybag_oa\"]\n\tpath = cybag_oa\n    url = https://github.com/ucb-art/cybag_oa.git\n"
  },
  {
    "path": "LICENSE",
    "chars": 1539,
    "preview": "BSD 3-Clause License\n\nCopyright (c) 2018, Regents of the University of California\nAll rights reserved.\n\nRedistribution a"
  },
  {
    "path": "README.md",
    "chars": 361,
    "preview": "Berkeley Analog Generator (BAG) version 2.0 and later.\n\nBAG 2.0 is a complete rewrite of BAG 1.x (which is in pre-alpha "
  },
  {
    "path": "bag/LICENSE",
    "chars": 1539,
    "preview": "BSD 3-Clause License\n\nCopyright (c) 2018, Regents of the University of California\nAll rights reserved.\n\nRedistribution a"
  },
  {
    "path": "bag/__init__.py",
    "chars": 580,
    "preview": "# -*- coding: utf-8 -*-\n\n\"\"\"This is the bag root package.\n\"\"\"\n\nimport signal\n\nfrom . import math\nfrom .math import float"
  },
  {
    "path": "bag/concurrent/LICENSE",
    "chars": 1539,
    "preview": "BSD 3-Clause License\n\nCopyright (c) 2018, Regents of the University of California\nAll rights reserved.\n\nRedistribution a"
  },
  {
    "path": "bag/concurrent/__init__.py",
    "chars": 105,
    "preview": "# -*- coding: utf-8 -*-\n\n\"\"\"This package define helper classes used to perform concurrent operations.\n\"\"\""
  },
  {
    "path": "bag/concurrent/core.py",
    "chars": 11053,
    "preview": "# -*- coding: utf-8 -*-\n\n\"\"\"This module define utility classes for performing concurrent operations.\n\"\"\"\n\nfrom typing im"
  },
  {
    "path": "bag/core.py",
    "chars": 56412,
    "preview": "# -*- coding: utf-8 -*-\n\n\"\"\"This is the core bag module.\n\"\"\"\n\nfrom typing import TYPE_CHECKING, Dict, Any, Tuple, Option"
  },
  {
    "path": "bag/data/LICENSE",
    "chars": 1539,
    "preview": "BSD 3-Clause License\n\nCopyright (c) 2018, Regents of the University of California\nAll rights reserved.\n\nRedistribution a"
  },
  {
    "path": "bag/data/__init__.py",
    "chars": 319,
    "preview": "# -*- coding: utf-8 -*-\n\n\"\"\"This package defines methods and classes useful for data post-processing.\n\"\"\"\n\n# compatibili"
  },
  {
    "path": "bag/data/core.py",
    "chars": 11281,
    "preview": "# -*- coding: utf-8 -*-\n\n\"\"\"This module defines core data post-processing classes.\n\"\"\"\n\nimport numpy as np\nimport scipy."
  },
  {
    "path": "bag/data/dc.py",
    "chars": 8114,
    "preview": "# -*- coding: utf-8 -*-\n\n\"\"\"This module defines classes for computing DC operating point.\n\"\"\"\n\nfrom typing import Union,"
  },
  {
    "path": "bag/data/digital.py",
    "chars": 7802,
    "preview": "# -*- coding: utf-8 -*-\n\n\"\"\"This module defines functions useful for digital verification/postprocessing.\n\"\"\"\n\nfrom typi"
  },
  {
    "path": "bag/data/lti.py",
    "chars": 30576,
    "preview": "# -*- coding: utf-8 -*-\n\n\"\"\"This module defines functions and classes useful for characterizing linear time-invariant ci"
  },
  {
    "path": "bag/data/ltv.py",
    "chars": 17833,
    "preview": "# -*- coding: utf-8 -*-\n\n\"\"\"This module defines functions and classes for linear time-varying circuits data post-process"
  },
  {
    "path": "bag/data/mos.py",
    "chars": 2940,
    "preview": "# -*- coding: utf-8 -*-\n\n\"\"\"This module defines classes for computing DC operating point.\n\"\"\"\n\nfrom typing import Dict\n\n"
  },
  {
    "path": "bag/data/plot.py",
    "chars": 21053,
    "preview": "# -*- coding: utf-8 -*-\n\n\"\"\"This module contains utilities to improve waveform plotting in python.\n\"\"\"\n\nimport numpy as "
  },
  {
    "path": "bag/design/LICENSE",
    "chars": 1539,
    "preview": "BSD 3-Clause License\n\nCopyright (c) 2018, Regents of the University of California\nAll rights reserved.\n\nRedistribution a"
  },
  {
    "path": "bag/design/__init__.py",
    "chars": 292,
    "preview": "# -*- coding: utf-8 -*-\n\n\"\"\"This package defines design template classes.\n\"\"\"\n\nfrom .module import Module, ModuleDB, Sch"
  },
  {
    "path": "bag/design/module.py",
    "chars": 38109,
    "preview": "# -*- coding: utf-8 -*-\n\n\"\"\"This module defines base design module class and primitive design classes.\n\"\"\"\n\nimport os\nim"
  },
  {
    "path": "bag/interface/LICENSE",
    "chars": 1539,
    "preview": "BSD 3-Clause License\n\nCopyright (c) 2018, Regents of the University of California\nAll rights reserved.\n\nRedistribution a"
  },
  {
    "path": "bag/interface/__init__.py",
    "chars": 250,
    "preview": "# -*- coding: utf-8 -*-\n\n\"\"\"This packages defines classes to interface with CAD database and circuit simulators.\n\"\"\"\n\nfr"
  },
  {
    "path": "bag/interface/base.py",
    "chars": 663,
    "preview": "# -*- coding: utf-8 -*-\n\n\"\"\"This module defines the base of all interface classes.\n\"\"\"\n\nfrom typing import Dict, Any\n\nfr"
  },
  {
    "path": "bag/interface/database.py",
    "chars": 21388,
    "preview": "# -*- coding: utf-8 -*-\n\n\"\"\"This module defines DbAccess, the base class for CAD database manipulation.\n\"\"\"\n\nfrom typing"
  },
  {
    "path": "bag/interface/ocean.py",
    "chars": 6686,
    "preview": "# -*- coding: utf-8 -*-\n\n\"\"\"This module implements bag's interaction with an ocean simulator.\n\"\"\"\n\nfrom typing import TY"
  },
  {
    "path": "bag/interface/server.py",
    "chars": 9212,
    "preview": "# -*- coding: utf-8 -*-\n\n\"\"\"This class defines SkillOceanServer, a server that handles skill/ocean requests.\n\nThe SkillO"
  },
  {
    "path": "bag/interface/simulator.py",
    "chars": 7833,
    "preview": "# -*- coding: utf-8 -*-\n\n\"\"\"This module handles high level simulation routines.\n\nThis module defines SimAccess, which pr"
  },
  {
    "path": "bag/interface/skill.py",
    "chars": 22736,
    "preview": "# -*- coding: utf-8 -*-\n\n\"\"\"This module implements all CAD database manipulations using skill commands.\n\"\"\"\n\nfrom typing"
  },
  {
    "path": "bag/interface/templates/LICENSE",
    "chars": 1539,
    "preview": "BSD 3-Clause License\n\nCopyright (c) 2018, Regents of the University of California\nAll rights reserved.\n\nRedistribution a"
  },
  {
    "path": "bag/interface/templates/Module.pyi",
    "chars": 1557,
    "preview": "# -*- coding: utf-8 -*-\n\nfrom typing import Dict\n\nimport os\nimport pkg_resources\n\nfrom bag.design.module import Module\n\n"
  },
  {
    "path": "bag/interface/templates/PrimModule.pyi",
    "chars": 650,
    "preview": "# -*- coding: utf-8 -*-\n\nimport os\nimport pkg_resources\n\nfrom bag.design.module import {{ module_name }}\n\n\n# noinspectio"
  },
  {
    "path": "bag/interface/templates/calibreview_setup.txt",
    "chars": 575,
    "preview": "calibre_view_netlist_file : {{ netlist_file }}\noutput_library : {{ lib_name }}\nschematic_library : {{ lib_name }}\ncell_n"
  },
  {
    "path": "bag/interface/templates/load_results.ocn",
    "chars": 6839,
    "preview": "lib = \"{{ lib }}\"\ncell = \"{{ cell }}\"\nview = \"{{ view }}\"\ninit_file = \"{{ init_file }}\"\nsave_dir = \"{{ save_dir }}\"\nprec"
  },
  {
    "path": "bag/interface/templates/run_simulation.ocn",
    "chars": 7176,
    "preview": "lib = \"{{ lib }}\"\ncell = \"{{ cell }}\"\nview = \"{{ view }}\"\nstate = \"{{ state }}\"\ninit_file = \"{{ init_file }}\"\nsave_dir ="
  },
  {
    "path": "bag/interface/zmqwrapper.py",
    "chars": 9367,
    "preview": "# -*- coding: utf-8 -*-\n\n\"\"\"This module defines various wrapper around ZMQ sockets.\"\"\"\n\nfrom datetime import datetime\nim"
  },
  {
    "path": "bag/io/LICENSE",
    "chars": 1539,
    "preview": "BSD 3-Clause License\n\nCopyright (c) 2018, Regents of the University of California\nAll rights reserved.\n\nRedistribution a"
  },
  {
    "path": "bag/io/__init__.py",
    "chars": 972,
    "preview": "# -*- coding: utf-8 -*-\n\n\"\"\"This package provides all IO related functionalities for BAG.\n\nMost importantly, this module"
  },
  {
    "path": "bag/io/common.py",
    "chars": 2269,
    "preview": "# -*- coding: utf-8 -*-\n\n\"\"\"This module contains some commonly used IO functions.\n\nIn particular, this module keeps trac"
  },
  {
    "path": "bag/io/file.py",
    "chars": 5207,
    "preview": "# -*- coding: utf-8 -*-\n\n\"\"\"This module handles file related IO.\n\"\"\"\nfrom typing import Dict, Any\n\nimport os\nimport temp"
  },
  {
    "path": "bag/io/gui.py",
    "chars": 7366,
    "preview": "# -*- coding: utf-8 -*-\n\nimport os\nimport sys\nimport subprocess\nimport json\nimport select\n\nimport PyQt5.QtWidgets as QtW"
  },
  {
    "path": "bag/io/process.py",
    "chars": 12961,
    "preview": "# -*- coding: utf-8 -*-\n\n\"\"\"This module provides functions to help you run external processes.\n\"\"\"\n\nimport os\nimport sys"
  },
  {
    "path": "bag/io/sim_data.py",
    "chars": 8592,
    "preview": "# -*- coding: utf-8 -*-\n\n\"\"\"This module handles simulation data related IO.\n\nNote : when reading data files, we use Nump"
  },
  {
    "path": "bag/io/template.py",
    "chars": 616,
    "preview": "# -*- coding: utf-8 -*-\n\n\"\"\"This module defines methods to create files from templates.\n\"\"\"\n\nfrom jinja2 import Environm"
  },
  {
    "path": "bag/layout/LICENSE",
    "chars": 1539,
    "preview": "BSD 3-Clause License\n\nCopyright (c) 2018, Regents of the University of California\nAll rights reserved.\n\nRedistribution a"
  },
  {
    "path": "bag/layout/__init__.py",
    "chars": 313,
    "preview": "# -*- coding: utf-8 -*-\n\n\"\"\"This package contains code for templated based layout.\n\"\"\"\n\nfrom .core import BagLayout, Tec"
  },
  {
    "path": "bag/layout/core.py",
    "chars": 64120,
    "preview": "# -*- coding: utf-8 -*-\n\n\"\"\"This module defines the base template class.\n\"\"\"\n\nfrom typing import Dict, List, Iterator, T"
  },
  {
    "path": "bag/layout/digital.py",
    "chars": 19858,
    "preview": "# -*- coding: utf-8 -*-\n\n\"\"\"This module defines layout template classes for digital standard cells.\n\"\"\"\n\nfrom typing imp"
  },
  {
    "path": "bag/layout/objects.py",
    "chars": 81527,
    "preview": "# -*- coding: utf-8 -*-\n\n\"\"\"This module defines various layout objects one can add and manipulate in a template.\n\"\"\"\nfro"
  },
  {
    "path": "bag/layout/routing/LICENSE",
    "chars": 1539,
    "preview": "BSD 3-Clause License\n\nCopyright (c) 2018, Regents of the University of California\nAll rights reserved.\n\nRedistribution a"
  },
  {
    "path": "bag/layout/routing/__init__.py",
    "chars": 187,
    "preview": "# -*- coding: utf-8 -*-\n\n\"\"\"This package provide routing classes.\n\"\"\"\n\nfrom .base import TrackID, WireArray, Port, Track"
  },
  {
    "path": "bag/layout/routing/base.py",
    "chars": 33583,
    "preview": "# -*- coding: utf-8 -*-\n\n\"\"\"This module provides basic routing classes.\n\"\"\"\n\nfrom typing import Tuple, Union, Generator,"
  },
  {
    "path": "bag/layout/routing/fill.py",
    "chars": 38386,
    "preview": "# -*- coding: utf-8 -*-\n\n\"\"\"This module defines classes that provides automatic fill utility on a grid.\n\"\"\"\n\nfrom typing"
  },
  {
    "path": "bag/layout/routing/grid.py",
    "chars": 69959,
    "preview": "# -*- coding: utf-8 -*-\n\n\"\"\"This module defines the RoutingGrid class.\n\"\"\"\n\nfrom typing import TYPE_CHECKING, Sequence, "
  },
  {
    "path": "bag/layout/tech.py",
    "chars": 11020,
    "preview": "# -*- coding: utf-8 -*-\n\nfrom typing import List, Tuple, Union, Optional, Callable, TYPE_CHECKING\n\nimport abc\n\nfrom .cor"
  },
  {
    "path": "bag/layout/template.py",
    "chars": 183914,
    "preview": "# -*- coding: utf-8 -*-\n\n\"\"\"This module defines layout template classes.\n\"\"\"\n\nfrom typing import TYPE_CHECKING, Union, D"
  },
  {
    "path": "bag/layout/util.py",
    "chars": 31740,
    "preview": "# -*- coding: utf-8 -*-\n\n\"\"\"This module contains utility classes used for layout\n\"\"\"\n\nfrom typing import Iterator, Union"
  },
  {
    "path": "bag/math/LICENSE",
    "chars": 1539,
    "preview": "BSD 3-Clause License\n\nCopyright (c) 2018, Regents of the University of California\nAll rights reserved.\n\nRedistribution a"
  },
  {
    "path": "bag/math/__init__.py",
    "chars": 2385,
    "preview": "# -*- coding: utf-8 -*-\n\n\"\"\"This package defines design template classes.\n\"\"\"\n\nfrom typing import Iterable\n\nimport numpy"
  },
  {
    "path": "bag/math/dfun.py",
    "chars": 20893,
    "preview": "# -*- coding: utf-8 -*-\n\n\"\"\"This module defines the differentiable function class.\"\"\"\n\nfrom typing import Union, List, O"
  },
  {
    "path": "bag/math/interpolate.py",
    "chars": 19665,
    "preview": "# -*- coding: utf-8 -*-\n\n\"\"\"This module defines various interpolation classes.\n\"\"\"\n\nfrom typing import List, Tuple, Unio"
  },
  {
    "path": "bag/mdao/LICENSE",
    "chars": 1539,
    "preview": "BSD 3-Clause License\n\nCopyright (c) 2018, Regents of the University of California\nAll rights reserved.\n\nRedistribution a"
  },
  {
    "path": "bag/mdao/__init__.py",
    "chars": 87,
    "preview": "# -*- coding: utf-8 -*-\n\n\"\"\"This package contains various openmdao related modules.\n\"\"\""
  },
  {
    "path": "bag/mdao/components.py",
    "chars": 4736,
    "preview": "# -*- coding: utf-8 -*-\n\n\"\"\"This module defines various OpenMDAO component classes.\n\"\"\"\n\nimport numpy as np\nimport openm"
  },
  {
    "path": "bag/mdao/core.py",
    "chars": 9381,
    "preview": "# -*- coding: utf-8 -*-\n\n\"\"\"This module defines core BAG openmdao classes.\"\"\"\n\nimport numpy as np\nimport networkx as nx\n"
  },
  {
    "path": "bag/simulation/LICENSE",
    "chars": 1539,
    "preview": "BSD 3-Clause License\n\nCopyright (c) 2018, Regents of the University of California\nAll rights reserved.\n\nRedistribution a"
  },
  {
    "path": "bag/simulation/__init__.py",
    "chars": 126,
    "preview": "# -*- coding: utf-8 -*-\n\n\"\"\"This package defines various utility classes for running simulations and data post-processin"
  },
  {
    "path": "bag/simulation/core.py",
    "chars": 29600,
    "preview": "# -*- coding: utf-8 -*-\n\nfrom typing import TYPE_CHECKING, Optional, Dict, Any, Tuple, List, Iterable, Sequence\n\nimport "
  },
  {
    "path": "bag/simulation/core_v2.py",
    "chars": 11844,
    "preview": "from __future__ import annotations\nfrom typing import (\n    TYPE_CHECKING, Optional, Dict, Any, Type, cast, List\n)\n\nimpo"
  },
  {
    "path": "bag/tech/LICENSE",
    "chars": 1539,
    "preview": "BSD 3-Clause License\n\nCopyright (c) 2018, Regents of the University of California\nAll rights reserved.\n\nRedistribution a"
  },
  {
    "path": "bag/tech/__init__.py",
    "chars": 128,
    "preview": "# -*- coding: utf-8 -*-\n\n\"\"\"This package contains various technology related utilities, such as transistor characterizat"
  },
  {
    "path": "bag/tech/core.py",
    "chars": 35160,
    "preview": "# -*- coding: utf-8 -*-\n\n\"\"\"This module contains commonly used technology related classes and functions.\n\"\"\"\n\nimport os\n"
  },
  {
    "path": "bag/tech/mos.py",
    "chars": 10656,
    "preview": "# -*- coding: utf-8 -*-\n\n\"\"\"This module contains transistor characterization and optimization related classes.\n\"\"\"\n\nimpo"
  },
  {
    "path": "bag/util/LICENSE",
    "chars": 1539,
    "preview": "BSD 3-Clause License\n\nCopyright (c) 2018, Regents of the University of California\nAll rights reserved.\n\nRedistribution a"
  },
  {
    "path": "bag/util/__init__.py",
    "chars": 79,
    "preview": "# -*- coding: utf-8 -*-\n\n\"\"\"This package defines various utilities classes.\n\"\"\""
  },
  {
    "path": "bag/util/cache.py",
    "chars": 25251,
    "preview": "# -*- coding: utf-8 -*-\n\n\"\"\"This module defines classes used to cache existing design masters\n\"\"\"\n\nfrom typing import Se"
  },
  {
    "path": "bag/util/immutable.py",
    "chars": 5950,
    "preview": "\"\"\"This module defines various immutable and hashable data types.\n\"\"\"\n\nfrom __future__ import annotations\n\nfrom typing i"
  },
  {
    "path": "bag/util/interval.py",
    "chars": 19824,
    "preview": "# -*- coding: utf-8 -*-\n\n\"\"\"This module provides data structure that keeps track of intervals.\n\"\"\"\n\nfrom typing import L"
  },
  {
    "path": "bag/util/parse.py",
    "chars": 1238,
    "preview": "# -*- coding: utf-8 -*-\n\n\"\"\"This module defines parsing utility methods.\n\"\"\"\n\nimport ast\n\n\nclass ExprVarScanner(ast.Node"
  },
  {
    "path": "bag/util/search.py",
    "chars": 18344,
    "preview": "# -*- coding: utf-8 -*-\n\n\"\"\"This module provides search related utilities.\n\"\"\"\n\nfrom typing import Optional, Callable, A"
  },
  {
    "path": "bag/verification/LICENSE",
    "chars": 1539,
    "preview": "BSD 3-Clause License\n\nCopyright (c) 2018, Regents of the University of California\nAll rights reserved.\n\nRedistribution a"
  },
  {
    "path": "bag/verification/__init__.py",
    "chars": 793,
    "preview": "# -*- coding: utf-8 -*-\n\n\"\"\"This package contains LVS/RCX related verification methods.\n\"\"\"\n\nfrom typing import Any\n\nimp"
  },
  {
    "path": "bag/verification/base.py",
    "chars": 13412,
    "preview": "# -*- coding: utf-8 -*-\n\n\"\"\"This module defines Checker, an abstract base class that handles LVS/RCX.\"\"\"\n\nfrom typing im"
  },
  {
    "path": "bag/verification/calibre.py",
    "chars": 23792,
    "preview": "# -*- coding: utf-8 -*-\n\n\"\"\"This module implements LVS/RCX using Calibre and stream out from Virtuoso.\n\"\"\"\n\nfrom typing "
  },
  {
    "path": "bag/verification/icv.py",
    "chars": 12655,
    "preview": "# -*- coding: utf-8 -*-\n\n\"\"\"This module implements LVS/RCX using ICV and stream out from Virtuoso.\n\"\"\"\n\nfrom typing impo"
  },
  {
    "path": "bag/verification/pvs.py",
    "chars": 11239,
    "preview": "# -*- coding: utf-8 -*-\n\n\"\"\"This module implements LVS/RCX using PVS/QRC and stream out from Virtuoso.\n\"\"\"\n\nfrom typing "
  },
  {
    "path": "bag/verification/templates/LICENSE",
    "chars": 1539,
    "preview": "BSD 3-Clause License\n\nCopyright (c) 2018, Regents of the University of California\nAll rights reserved.\n\nRedistribution a"
  },
  {
    "path": "bag/verification/templates/layout_export_config.txt",
    "chars": 1641,
    "preview": "case                               \"preserve\"\ncellListFile                       \"\"\ncellMap                            \""
  },
  {
    "path": "bag/verification/templates/si_env.txt",
    "chars": 659,
    "preview": "simStopList = '(\"auCdl\")\nsimViewList = '(\"auCdl\" \"schematic\")\nglobalGndSig = \"\"\nglobalPowerSig = \"\"\nshrinkFACTOR = 0\nche"
  },
  {
    "path": "bag/verification/virtuoso.py",
    "chars": 3657,
    "preview": "# -*- coding: utf-8 -*-\n\n\"\"\"This module handles exporting schematic/layout from Virtuoso.\n\"\"\"\n\nfrom typing import TYPE_C"
  },
  {
    "path": "bag/virtuoso.py",
    "chars": 3891,
    "preview": "# -*- coding: utf-8 -*-\n\n\"\"\"This module provides functions needed to get Virtuoso to work with BAG.\n\"\"\"\n\nimport os\nimpor"
  },
  {
    "path": "docs/.gitignore",
    "chars": 6,
    "preview": "build\n"
  },
  {
    "path": "docs/LICENSE",
    "chars": 1539,
    "preview": "BSD 3-Clause License\n\nCopyright (c) 2018, Regents of the University of California\nAll rights reserved.\n\nRedistribution a"
  },
  {
    "path": "docs/Makefile",
    "chars": 8061,
    "preview": "# Makefile for Sphinx documentation\n#\n\n# You can set these variables from the command line.\nSPHINXOPTS    =\nSPHINXBUILD "
  },
  {
    "path": "docs/README",
    "chars": 219,
    "preview": "To build/update documentation:\n\n1. make sure BAG Python's bin folder is in your path.\n\n2. run: \n\n   ./refresh_api.sh\n   "
  },
  {
    "path": "docs/refresh_api.sh",
    "chars": 74,
    "preview": "#!/usr/bin/env tcsh\n\nsphinx-apidoc --force --output-dir=source/api ../bag\n"
  },
  {
    "path": "docs/source/LICENSE",
    "chars": 1539,
    "preview": "BSD 3-Clause License\n\nCopyright (c) 2018, Regents of the University of California\nAll rights reserved.\n\nRedistribution a"
  },
  {
    "path": "docs/source/api/LICENSE",
    "chars": 1539,
    "preview": "BSD 3-Clause License\n\nCopyright (c) 2018, Regents of the University of California\nAll rights reserved.\n\nRedistribution a"
  },
  {
    "path": "docs/source/api/bag.data.rst",
    "chars": 1085,
    "preview": "bag.data package\n================\n\nSubmodules\n----------\n\nbag.data.core module\n--------------------\n\n.. automodule:: bag"
  },
  {
    "path": "docs/source/api/bag.design.rst",
    "chars": 472,
    "preview": "bag.design package\n==================\n\nSubmodules\n----------\n\nbag.design.database module\n--------------------------\n\n.. "
  },
  {
    "path": "docs/source/api/bag.interface.rst",
    "chars": 1122,
    "preview": "bag.interface package\n=====================\n\nSubmodules\n----------\n\nbag.interface.database module\n----------------------"
  },
  {
    "path": "docs/source/api/bag.io.rst",
    "chars": 817,
    "preview": "bag.io package\n==============\n\nSubmodules\n----------\n\nbag.io.common module\n--------------------\n\n.. automodule:: bag.io."
  },
  {
    "path": "docs/source/api/bag.layout.routing.rst",
    "chars": 687,
    "preview": "bag.layout.routing package\n==========================\n\nSubmodules\n----------\n\nbag.layout.routing.base module\n-----------"
  },
  {
    "path": "docs/source/api/bag.layout.rst",
    "chars": 1113,
    "preview": "bag.layout package\n==================\n\nSubpackages\n-----------\n\n.. toctree::\n\n    bag.layout.routing\n\nSubmodules\n-------"
  },
  {
    "path": "docs/source/api/bag.math.rst",
    "chars": 457,
    "preview": "bag.math package\n================\n\nSubmodules\n----------\n\nbag.math.dfun module\n--------------------\n\n.. automodule:: bag"
  },
  {
    "path": "docs/source/api/bag.mdao.rst",
    "chars": 454,
    "preview": "bag.mdao package\n================\n\nSubmodules\n----------\n\nbag.mdao.components module\n--------------------------\n\n.. auto"
  },
  {
    "path": "docs/source/api/bag.rst",
    "chars": 588,
    "preview": "bag package\n===========\n\nSubpackages\n-----------\n\n.. toctree::\n\n    bag.data\n    bag.design\n    bag.interface\n    bag.io"
  },
  {
    "path": "docs/source/api/bag.tech.rst",
    "chars": 433,
    "preview": "bag.tech package\n================\n\nSubmodules\n----------\n\nbag.tech.core module\n--------------------\n\n.. automodule:: bag"
  },
  {
    "path": "docs/source/api/bag.util.rst",
    "chars": 734,
    "preview": "bag.util package\n================\n\nSubmodules\n----------\n\nbag.util.interval module\n------------------------\n\n.. automodu"
  },
  {
    "path": "docs/source/api/bag.verification.rst",
    "chars": 857,
    "preview": "bag.verification package\n========================\n\nSubmodules\n----------\n\nbag.verification.base module\n-----------------"
  },
  {
    "path": "docs/source/api/modules.rst",
    "chars": 46,
    "preview": "bag\n===\n\n.. toctree::\n   :maxdepth: 4\n\n   bag\n"
  },
  {
    "path": "docs/source/conf.py",
    "chars": 10043,
    "preview": "# -*- coding: utf-8 -*-\n#\n# BAG documentation build configuration file, created by\n# sphinx-quickstart on Fri May 27 15:"
  },
  {
    "path": "docs/source/developer/LICENSE",
    "chars": 1539,
    "preview": "BSD 3-Clause License\n\nCopyright (c) 2018, Regents of the University of California\nAll rights reserved.\n\nRedistribution a"
  },
  {
    "path": "docs/source/developer/developer.rst",
    "chars": 53,
    "preview": "Developer Guide\n===============\n\nNothing here yet...\n"
  },
  {
    "path": "docs/source/index.rst",
    "chars": 508,
    "preview": ".. BAG documentation master file, created by\n   sphinx-quickstart on Fri May 27 15:45:44 2016.\n   You can adapt this fil"
  },
  {
    "path": "docs/source/overview/LICENSE",
    "chars": 1539,
    "preview": "BSD 3-Clause License\n\nCopyright (c) 2018, Regents of the University of California\nAll rights reserved.\n\nRedistribution a"
  },
  {
    "path": "docs/source/overview/design.rst",
    "chars": 5222,
    "preview": "Design Module\n=============\n\nA design module is a Python class that generates new schematics.  It computes all parameter"
  },
  {
    "path": "docs/source/overview/overview.rst",
    "chars": 2066,
    "preview": "Overview\n========\n\n.. figure:: ./figures/bag_flow.png\n    :align: center\n    :figclass: align-center\n\n    BAG design flo"
  },
  {
    "path": "docs/source/overview/schematic.rst",
    "chars": 2195,
    "preview": "Schematic Generator\n===================\n\nA schematic generator is a schematic in your CAD program that tells BAG all the"
  },
  {
    "path": "docs/source/overview/testbench.rst",
    "chars": 2464,
    "preview": "Testbench Generator\n===================\n\nA testbench generator is just a normal testbench with schematic and adexl view."
  },
  {
    "path": "docs/source/setup/LICENSE",
    "chars": 1539,
    "preview": "BSD 3-Clause License\n\nCopyright (c) 2018, Regents of the University of California\nAll rights reserved.\n\nRedistribution a"
  },
  {
    "path": "docs/source/setup/bag_config/LICENSE",
    "chars": 1539,
    "preview": "BSD 3-Clause License\n\nCopyright (c) 2018, Regents of the University of California\nAll rights reserved.\n\nRedistribution a"
  },
  {
    "path": "docs/source/setup/bag_config/bag_config.rst",
    "chars": 329,
    "preview": "BAG Configuration File\n======================\n\nBAG configuration file is written in YAML format.  This document describe"
  },
  {
    "path": "docs/source/setup/bag_config/database/database.rst",
    "chars": 5613,
    "preview": "database\n========\n\nThis entry defines all settings related to Virtuoso.\n\n\ndata.class\n----------\n\nThe Python class that h"
  },
  {
    "path": "docs/source/setup/bag_config/misc.rst",
    "chars": 988,
    "preview": "class\n=====\n\nThe subclass of :ref:\n\n\n.. _bag_lib_defs:\n\nlib_defs\n========\n\nLocation of the BAG design module libraries d"
  },
  {
    "path": "docs/source/setup/bag_config/simulation/simulation.rst",
    "chars": 1651,
    "preview": "simulation\n==========\n\nThis entry defines all settings related to Ocean.\n\nsimulation.class\n----------------\n\nThe Python "
  },
  {
    "path": "docs/source/setup/bag_config/socket/socket.rst",
    "chars": 1079,
    "preview": "socket\n======\n\nThis entry defines socket settings for BAG to communicate with Virtuoso.\n\nsocket.host\n-----------\n\nThe ho"
  },
  {
    "path": "docs/source/setup/config_summary.rst",
    "chars": 1639,
    "preview": "Configuration Files Summary\n===========================\n\nAlthough BAG has many configuration settings, most of them do n"
  },
  {
    "path": "docs/source/setup/install_python.rst",
    "chars": 2648,
    "preview": "Installing Python for BAG\n==========================\n\nThis section describes how to install Python for running BAG.\n\nIns"
  },
  {
    "path": "docs/source/setup/new_pdk.rst",
    "chars": 1309,
    "preview": "Setting up New PDK\n==================\n\nThis section describes how to get BAG 2.0 to work with a new PDK.\n\n#. Create a ne"
  },
  {
    "path": "docs/source/setup/pyoptsparse.rst",
    "chars": 58,
    "preview": "Building Pyoptsparse\n====================\n\nTo be written.\n"
  },
  {
    "path": "docs/source/setup/setup.rst",
    "chars": 491,
    "preview": "BAG Setup Procedure\n===================\n\nThis document describes how to install Python for BAG and the various configura"
  },
  {
    "path": "docs/source/setup/tech_config/LICENSE",
    "chars": 1539,
    "preview": "BSD 3-Clause License\n\nCopyright (c) 2018, Regents of the University of California\nAll rights reserved.\n\nRedistribution a"
  },
  {
    "path": "docs/source/setup/tech_config/layout/layout.rst",
    "chars": 213,
    "preview": "layout\n======\n\nThis entry defines all layout specific settings.\n\n\nlayout.em_temp\n--------------\n\nThe temperature used to"
  },
  {
    "path": "docs/source/setup/tech_config/misc.rst",
    "chars": 253,
    "preview": ".. _tech_config_path:\n\nclass\n=====\n\nThe subclass of :class:`bag.layout.core.TechInfo` for this process technology.\nIf th"
  },
  {
    "path": "docs/source/setup/tech_config/mos/mos.rst",
    "chars": 386,
    "preview": "mos\n===\n\nThis entry defines all MOS transistor settings.\n\n\nmos.width_resolution\n--------------------\n\nThe transistor wid"
  },
  {
    "path": "docs/source/setup/tech_config/tech_config.rst",
    "chars": 321,
    "preview": "Technology Configuration File\n=============================\n\nTechnology configuration file is written in YAML format.  T"
  },
  {
    "path": "docs/source/tutorial/LICENSE",
    "chars": 1539,
    "preview": "BSD 3-Clause License\n\nCopyright (c) 2018, Regents of the University of California\nAll rights reserved.\n\nRedistribution a"
  },
  {
    "path": "docs/source/tutorial/figures/LICENSE",
    "chars": 1539,
    "preview": "BSD 3-Clause License\n\nCopyright (c) 2018, Regents of the University of California\nAll rights reserved.\n\nRedistribution a"
  },
  {
    "path": "docs/source/tutorial/tutorial.rst",
    "chars": 594,
    "preview": "Tutorial\n========\n\nThis section contains several simple tutorials for you to get an idea of the BAG workflow.\n\nIn these "
  },
  {
    "path": "run_scripts/LICENSE",
    "chars": 1539,
    "preview": "BSD 3-Clause License\n\nCopyright (c) 2018, Regents of the University of California\nAll rights reserved.\n\nRedistribution a"
  },
  {
    "path": "run_scripts/clean_cds_lib.py",
    "chars": 2241,
    "preview": "\"\"\"\nThis script removes the '#Removed by ddDeleteObj' items in cds.lib.\nIt is also capable of taking a list of patterns "
  },
  {
    "path": "run_scripts/compile_verilog.il",
    "chars": 653,
    "preview": "\nprocedure( compile_netlist_views(fname \"t\")\n    let( (p line info_list lib cell view obj cv)\n        unless( p = infile"
  },
  {
    "path": "run_scripts/gen_cell.py",
    "chars": 2839,
    "preview": "import argparse\nfrom argparse import Namespace\nfrom pathlib import Path\n\nfrom bag.io.file import Pickle, Yaml\nfrom bag.c"
  },
  {
    "path": "run_scripts/generate_verilog.py",
    "chars": 1004,
    "preview": "\nimport os\n\nimport yaml\nfrom jinja2 import Environment, FileSystemLoader\n\n\ndef run_main():\n    verilog_dir = 'verilog_mo"
  },
  {
    "path": "run_scripts/meas_cell.py",
    "chars": 2668,
    "preview": "import argparse\nfrom argparse import Namespace\nfrom pathlib import Path\nimport pdb\n\nfrom bag.io.file import Pickle, Yaml"
  },
  {
    "path": "run_scripts/run_bag.sh",
    "chars": 66,
    "preview": "#!/usr/bin/env bash\n\nsource .bashrc_pypath\n\nexec ${BAG_PYTHON} $@\n"
  },
  {
    "path": "run_scripts/setup_submodules.py",
    "chars": 5329,
    "preview": "#!/usr/bin/env bash\n\n# crazy black magic from:\n# https://unix.stackexchange.com/questions/20880/how-can-i-use-environmen"
  },
  {
    "path": "run_scripts/sim_cell.py",
    "chars": 2675,
    "preview": "import argparse\nfrom argparse import Namespace\nfrom pathlib import Path\nimport pdb\n\nfrom bag.io.file import Pickle, Yaml"
  },
  {
    "path": "run_scripts/start_bag.il",
    "chars": 81422,
    "preview": "/*  Note:\n\nDue to licensing reasons, this skill script is missing the function \nCCSinvokeCdfCallbacks() from Cadence sol"
  },
  {
    "path": "run_scripts/start_bag.sh",
    "chars": 137,
    "preview": "#!/usr/bin/env bash\n\nexport PYTHONPATH=\"\"\n\n# disable QT session manager warnings\nunset SESSION_MANAGER\n\nexec ${BAG_PYTHO"
  },
  {
    "path": "run_scripts/start_bag_ICADV12d3.il",
    "chars": 81699,
    "preview": "/*  Note:\n\nDue to licensing reasons, this skill script is missing the function \nCCSinvokeCdfCallbacks() from Cadence sol"
  },
  {
    "path": "run_scripts/virt_server.sh",
    "chars": 305,
    "preview": "#!/usr/bin/env bash\n\nexport PYTHONPATH=\"${BAG_FRAMEWORK}\"\n\nexport cmd=\"-m bag.virtuoso run_skill_server\"\nexport min_port"
  },
  {
    "path": "setup.py",
    "chars": 1139,
    "preview": "# -*- coding: utf-8 -*-\n\nfrom setuptools import setup, find_packages\n\n\nsetup(\n    name='bag',\n    version='2.0',\n    des"
  },
  {
    "path": "tests/LICENSE",
    "chars": 1539,
    "preview": "BSD 3-Clause License\n\nCopyright (c) 2018, Regents of the University of California\nAll rights reserved.\n\nRedistribution a"
  },
  {
    "path": "tests/__init__.py",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "tests/layout/LICENSE",
    "chars": 1539,
    "preview": "BSD 3-Clause License\n\nCopyright (c) 2018, Regents of the University of California\nAll rights reserved.\n\nRedistribution a"
  },
  {
    "path": "tests/layout/__init__.py",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "tests/layout/routing/LICENSE",
    "chars": 1539,
    "preview": "BSD 3-Clause License\n\nCopyright (c) 2018, Regents of the University of California\nAll rights reserved.\n\nRedistribution a"
  },
  {
    "path": "tests/layout/routing/__init__.py",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "tests/layout/routing/test_fill.py",
    "chars": 11014,
    "preview": "from itertools import product\n\nimport pytest\n\nfrom bag.layout.routing.fill import fill_symmetric_helper\n\n\ndef check_disj"
  }
]

About this extraction

This page contains the full source code of the ucb-art/BAG_framework GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 162 files (1.4 MB), approximately 339.6k tokens, and a symbol index with 1515 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!