\title{Operatial Amplifier Topology Repository for use with Python}
\author{Steven K Armour}
\maketitle

Table of Contents

1  Premise

2  Libarys

3  Setup

4  Ideal OpAmp

5  TestBench

6  Buffer

7  Inverting

8  Noninverting

9  Miller Capactive Integrator

10  Miller Inductive Integrator

11  Low Pass 1Pole

12  Miller Capacitive Differentiator

13  Miller Inductive Differentiator

14  High Pass 1Zero

15  AllPass (Phase) Filter

15.1  First Order Cap First

15.2  First Order Cap Second

16  Sallen-Key Filter

16.1  Low Pass with Voltage Gain

16.2  Band Pass with Voltage Gain

16.3  HIgh Pass with Voltage Gain

Note:
This notebook uses the `ipywidgets` to provided a interactive exploration experience. In order to make use of this experience, this notebook must be downloaded and run on a computer what has `ipywidgets` installed and activated for directions for that see http://ipywidgets.readthedocs.io/en/latest/user_install.html

this notebook also uses the [`(some) LaTeX environments for Jupyter
`](http://jupyter-contrib-nbextensions.readthedocs.io/en/latest/nbextensions/latex_envs/README.html) found within the install of https://github.com/ipython-contrib/jupyter_contrib_nbextensions

And all diagrams are made in **draw.io** where the source file is `OpAmpTobosDrawing.html` where that file and the exported files should accompany this notebook from its GitHub source

# Premise
This Notebook serves as a Repository of Operational Amplifier Topologies for use in [PySpice](https://pyspice.fabrice-salvaire.fr/) and for the topologies theory. The Goal for the Author personally is to make one or two upgrades to this notebook a week. Whether that be a new feature, expanded theory, or new topology.

Some of the intended upgrades include:
\begin{itemize}
\item Enhanced analyses of output data
\item Routines to find figures of merits from output data
\item Added calculations from theory
\item Simulations from theory and compersion to SPICE sim
\item adding GUI interactivity via ipython notebook widgets
\end{itemize}

# Libarys

```python
from sympy import *
init_printing()
import ipywidgets as wgs

import numpy as np
import pandas as pd

import matplotlib.pyplot as plt
%matplotlib inline

from PySpice.Plot.BodeDiagram import bode_diagram
from PySpice.Probe.Plot import plot
from PySpice.Spice.Netlist import Circuit
from PySpice.Spice.Netlist import SubCircuitFactory
from PySpice.Unit import *

from ipywidgets import widgets
from IPython.display import display

#https://github.com/jrjohansson/version_information
%load_ext version_information
%version_information sympy, numpy, matplotlib, pandas, PySPice, ipywidgets
```

Software	Version

Python	3.6.4 64bit [GCC 7.2.0]

IPython	6.2.1

OS	Linux 4.13.0 32 generic x86_64 with debian stretch sid

sympy	1.1.1

numpy	1.13.3

matplotlib	2.1.1

pandas	0.21.1

PySPice	1.2.0

ipywidgets	7.2.1

Mon May 07 11:51:59 2018 MDT

# Setup

```python
def lambdify2(expresion, ReVars=False):
    """
    Helper Function to more automate sympy `lambdify` function

    Args:
        expresion: sympy expresion !Only Pass LHS of Equations
        ReVars (bool; False): Control to return the Vars in a list if True

    Note:
        lambdfy is set with `dummify=False` therefore do not pass
        unsantied variabls to functin else error will be thrown
    """
    #collect vars
    Vars=sorted(list(expresion.free_symbols), key=lambda x: str(x))
    #perform lambdfy with waver
    F=lambdify(Vars, expresion, dummify=False)
    if ReVars:
        return F, Vars
    else:
        return F
```

# Ideal OpAmp

Using the Semi Ideal OpAmp model devloped by Fabrice Salvaire for the OpAmp Exsample [8.10.3. Operational Amplifier](https://pyspice.fabrice-salvaire.fr/examples/operational-amplifier/operational-amplifier.html)

```python
class BasicOperationalAmplifier(SubCircuitFactory):
    """
    Semi Ideal OpAmp Model SubCir from
    https://pyspice.fabrice-salvaire.fr/examples/operational-amplifier/operational-amplifier.html
    A0=100e3; pole=100; A=10e6Hz
    Termanals:
        non_inverting_input
        inverting_input
        output
    """

    __name__ = 'BasicOperationalAmplifier'
    __nodes__ = ('non_inverting_input', 'inverting_input', 'output')

    ##############################################

    def __init__(self):

        super().__init__()

        # Input impedance
        self.R('input', 'non_inverting_input', 'inverting_input', 10@u_MΩ)

        # dc gain=100k and pole1=100hz
        # unity gain = dcgain x pole1 = 10MHZ
        self.VCVS('gain', 1, self.gnd, 'non_inverting_input', 'inverting_input', voltage_gain=kilo(100))
        self.R('P1', 1, 2, 1@u_kΩ)
        self.C('P1', 2, self.gnd, 1.5915@u_uF)

        # Output buffer and resistance
        self.VCVS('buffer', 3, self.gnd, 2, self.gnd, 1)
        self.R('out', 3, 'output', 10@u_Ω)
```

# TestBench

```python
class OnePortOpAmpTB(Circuit):
    """
    Class for implimenting Bode plot simulation for One Port
    (Two Termanal) Opertinal Amplifer topology
    """
    def __init__(self, DUT, **kwargs):
        """
        Args:
            DUT (class SubCircuitFactory): The One Port OpAmp topology class to be tested
            kwargs (dict): Addital Ciricut Parmters to the topology under test
        """
        #bind the DUT class to TB instance
        self.DUT=DUT
        #self.Theory=self.DUT.Theory()
        #create the ciruit name from DUT name
        super().__init__(title=f'{self.DUT.__name__}_TB', **kwargs)

    def SetupCir(self, **kwargs):
        """
        Sets up the the DUT and the Testbench for simulation
        see diagrm in source IPYNB for cirucit diagram
        Args:
            kwargs (dict): Addital Ciricut Parmters to the topology under test
        """
        #create instatince of DUT and setuo with parmaters
        self.DUT=self.DUT(**kwargs)

        #load the DUT in to the circuit
        self.subcircuit(self.DUT)
        self.X('Amp', self.DUT.__name__, 'Vin', 'Vout')

        #set up the TB elements
        self.R('Sload', 'Vin' , '1', 50@u_Ω)
        self.SinusoidalVoltageSource('Source', '1', self.gnd, amplitude=1@u_V)

        self.R('Tload', 'Vout', self.gnd, 50@u_Ω)
        #print out the resulting spice list
        print(self)

    def Simulate(self, fMin=1, fMax=20e6):
        """
        Method to perform a Bode plot simulation via NgSpice AC sim @25C
        Args:
            fMin(float; 1 [Hz]): the  starting frequnacy for
            Bode plot sweep in hertz

            fMax(float; 100e3[Hz]): the ending  frequnacy for
            Bode plot sweep in hertz

        Retured Attributes:
            results: the ac simulation raw results from NGSpice
            Data: Pandas Dataframe with ouputs and calcs from results

        """

        simulator = self.simulator(temperature=25, nominal_temperature=25)
        self.results = simulator.ac(start_frequency=fMin@u_Hz, stop_frequency=fMax@u_Hz, number_of_points=10,  variation='dec')

        # returned dataframe
        self.Data=pd.DataFrame()
        self.Data['freq[Hz]']=self.results.frequency
        self.Data['Vin']=self.results.Vin
        self.Data['Vout']=self.results.Vout
        #normlized gain in dB
        self.Data['Gain[dB]']=20*np.log10(np.abs(self.Data['Vout']/self.Data['Vin']))
        # phase in radians unwraped
        self.Data['Phase[rad_UW]']=(np.angle(self.Data['Vout']))

    def PlotResults(self):
        """
        Create a Bode plot from the simulation
        """
        self.fig, [self.axMag, self.axPh]=plt.subplots(ncols=1, nrows=2, sharex=True)

        A=self.Data['Gain[dB]']; F=self.Data['freq[Hz]']
        L=np.abs(np.abs(A-A.max())-3.0).idxmin()
        self.dB3=F[L]

        plt.suptitle("Bode Diagram of an Operational Amplifier")
        bode_diagram(axes=(self.axMag, self.axPh),
                     frequency=self.Data['freq[Hz]'],
                     gain=self.Data['Gain[dB]'],
                     phase=self.Data['Phase[rad_UW]'],
                     marker='.',
                     color='blue',
                     linestyle='-',
                    )

        self.axMag.axvline(x=self.dB3, label='3dB[Hz]',
                      linestyle='-.', alpha=.7, color='g')
        self.axPh.axvline(x=self.dB3, label='3dB[Hz]',
                      linestyle='-.', alpha=.7, color='g')

```

\begin{definition}
$A$ Open Loop Gain (no feedback)
$$A=\dfrac{V_o}{V_i}$$
\end{definition}

\begin{definition}
$G$ Closed Loop Gain (with feedback)
$$G=\dfrac{V_o}{V_i}$$
\end{definition}

\begin{definition}
$Z_{\text{i}}$ Open Loop Input Impedance (no feedback)
$$Z_{\text{i}}=\dfrac{V_{\text{diff}}}{I_i}$$
\end{definition}

\begin{definition}
$Z_{\text{o}}$ Open Loop Output Impedance (no feedback)
$$Z_{\text{o}}=\dfrac{V_o}{I_o}$$
\end{definition}

\begin{definition}
$Z_{\text{ifb}}$ Closed Loop Input Impedance (with feedback)
$$Z_{\text{ifb}}=\dfrac{V_i}{I_i}$$
\end{definition}

\begin{definition}
$Z_{\text{ofb}}$ Closed Loop Output Impedance (with feedback)
$$Z_{\text{ofb}}=\dfrac{V_o}{I_o}$$
\end{definition}

\begin{definition}
$V_{\text{diff}}$ the differential input voltage
$$V_{\text{diff}}=V_+-V_-$$
\end{definition}

# Buffer

[Analog Electronics: Circuits, Systems and Signal Processing](https://www.amazon.com/Analog-Electronics-Circuits-Systems-Processing-ebook/dp/B00CXO975A/ref=sr_1_1?ie=UTF8&qid=1524885102&sr=8-1&keywords=Analog+Electronics+Crecraft+Gergely) 3.5, 3.5.1, 3.5.2

$$V_{\text{diff}}=V_i-V_o$$
$$V_o=AV_{\text{diff}}$$
$$V_o=AV_i-AV_o$$
$$G=\dfrac{V_o}{V_i}=\dfrac{A}{1+A}\approx 1$$

$$I_i=\dfrac{V_{\text{diff}}}{Z_i} $$
$$V_{\text{diff}}=\dfrac{1}{1+A}V_i$$
$$I_i=\dfrac{1}{1+A} \dfrac{V_i}{Z_i}$$
$$Z_{\text{ifb}}=(1+A)Z_i$$

$$V_o=AV_{\text{diff}}-I_LZ_o$$
$$(1+A)V_o=AV_i-I_L Z_o$$
$$V_o=\dfrac{A}{1+A}V_i -\dfrac{Z_o}{1+A}I_L$$
$$V_o=GV_i -Z_{\text{ofb}}I_L$$
$$Z_{\text{ifb}}=\dfrac{Z_o}{1+A}$$

```python
class BufferOpAmp(SubCircuitFactory):
    """
    Buffer OpAmp SubCircuit
    Termanals:
        Vin
        Vout

    """

    __name__='BufferOpAmp'
    __nodes__=('Vin','Vout')

    def __init__(self):
        super().__init__()

        #'non_inverting_input', 'inverting_input', 'output'
        self.subcircuit(BasicOperationalAmplifier())
        self.X('op', 'BasicOperationalAmplifier', 'Vin', 'Vout', 'Vout')

    @staticmethod
    def Theory():
        """Analog Electronics: Circuits, Systems and Signal Processing
        Crecraft Gergely"""
        A,Zi, Zo, s=symbols('A, Z_i, Z_o s')
        H=Function('H')(s); Zin=Function('Z_ifb')(s); Zout=Function('Z_ofb')(s)
        TF=Eq(H, A/(1+A)); Zin=Eq(Zin, (1+A)*Zi); Zout=Eq(Zout, Zo/(1+A))
        return TF, Zin, Zout

```

```python
#Print out the Buffer Op Amp Theory
BufferOpAmp.Theory()
```

```python
#create the Test Bench and Bind to this instance the BufferOpAmp Topo
TB=OnePortOpAmpTB(BufferOpAmp)
#Setup the Testbench and print the TB
TB.SetupCir()
#Run the AC Bode Simulation
TB.Simulate()
#View Bode Simulation
TB.PlotResults()
```

# Inverting

```python
class InvertingOpAmp(SubCircuitFactory):
    """
    Inverting OpAmp SubCir
    Termanals:
        Vin
        Vout
    Parms:
        R1[Ohms]
        R2[Ohms]
    """

    __name__='InvertingOpAmp'
    __nodes__=('Vin','Vout')

    def __init__(self, R1=1, R2=1):
        super().__init__()
        self.R1=R1; self.R2=R2

        self.R('1', 'Vin', '2', R1@u_Ω)
        self.R('2', '2', 'Vout', R2@u_Ω)

        self.subcircuit(BasicOperationalAmplifier())
        self.X('op', 'BasicOperationalAmplifier', self.gnd, '2', 'Vout')
        self.Theory()

    @staticmethod
    def Theory():
        """Analog Electronics: Circuits, Systems and Signal Processing
        Crecraft Gergely"""
        R1, R2=symbols('R_1, R_2', real=True, postive=True)
        A,Zi, Zo, s=symbols('A, Z_i, Z_o s')
        H=Function('H')(s); Zin=Function('Z_in')(s); Zout=Function('Z_out')(s)
        TF=Eq(H, -R2/R1)
        Zin=Eq(Zin, R1)
        Zout=Eq(Zout, Zo*(R1+R2)/(A*R1))
        return TF, Zin, Zout

```

```python
#Print out the Inverting Op Amp Theory
InvertingOpAmp.Theory()
```

```python
class InvertingExp():
    """
    Class to provide GUI for Inverting Amplifer
    """
    def __init__(self):
        """
        Front end and binding to backend
        """
        #create the GUI elments
        self.R1Slider=widgets.FloatSlider(value=50,
                                         min=10, max=100, step=5,
                                         description='R1 kOhm',
                                         )
        self.R2Slider=widgets.FloatSlider(value=50,
                                         min=10, max=100, step=5,
                                         description='R2 kOhm',
                                         )
        #layout
        self.RSVBox=widgets.VBox([self.R1Slider, self.R2Slider])

        #bind to the backend
        I=widgets.interact_manual(self.BackEnd, R1=self.R1Slider,
                              R2=self.R2Slider)
        display(I)

    def BackEnd(self, R1, R2):
        """
        The backend wrapper to the TB and simulation
        """

        #create the Test Bench and Bind to this instance the InvertingOpAmp Topo
        TB=OnePortOpAmpTB(InvertingOpAmp)
        #Setup the Testbench; use a R1=20 Ohm,R2=5 Ohm and print the TB
        TB.SetupCir(**{'R1':R1*1e3, 'R2':R2*1e3})
        #Run the AC Bode Simulation
        TB.Simulate()
        #View Bode Simulation
        TB.PlotResults()

```

```python
InvertingExp();
```

    interactive(children=(FloatSlider(value=50.0, description='R1 kOhm', min=10.0, step=5.0), FloatSlider(value=50…

    <function ipywidgets.widgets.interaction._InteractFactory.__call__.<locals>.<lambda>>

# Noninverting

```python
class NonInvertingOpAmp(SubCircuitFactory):
    """
    Non-Inverting OpAmp SubCir
    Termanals:
        Vin
        Vout
    Parms:
        R1
        R2
    """

    __name__='NonInvertingOpAmp'
    __nodes__=('Vin','Vout')

    def __init__(self, R1, R2):
        super().__init__()
        self.R1=R1; self.R2=R2

        self.R('1', '2', self.gnd, R1@u_Ω)
        self.R('2', '2', 'Vout', R2@u_Ω)

        self.subcircuit(BasicOperationalAmplifier())
        self.X('op', 'BasicOperationalAmplifier', 'Vin', '2', 'Vout')

    @staticmethod
    def Theory():
        """Analog Electronics: Circuits, Systems and Signal Processing
        Crecraft Gergely"""
        R1, R2=symbols('R_1, R_2', real=True, postive=True)
        A,Zi, Zo, s=symbols('A, Z_i, Z_o s')
        H=Function('H')(s); Zin=Function('Z_in')(s); Zout=Function('Z_out')(s)
        TF=Eq(H, (A(R1+R2))/(R2(1+A)+R1))
        Zin=Eq(Zin, Zi*((1+A)*R2+R1)/(R2+(1-A)*R1))
        Zout=Eq(Zout, Zo*(R1+R2)/(R1+(1+A)*R2))
        return TF, Zin, Zout

```

```python
#Print out the NonInverting Op Amp Theory
NonInvertingOpAmp.Theory()
```

```python
class NonInvertingExp():
    """
    Class to provide GUI for Inverting Amplifer
    """
    def __init__(self):
        """
        Front end and binding to backend
        """
        #create the GUI elments
        self.R1Slider=widgets.FloatSlider(value=50,
                                         min=10, max=100, step=5,
                                         description='R1 kOhm'
                                         )
        self.R2Slider=widgets.FloatSlider(value=50,
                                         min=10, max=100, step=5,
                                         description='R2 kOhm'
                                         )
        #layout
        self.RSVBox=widgets.VBox([self.R1Slider, self.R2Slider])

        #bind to the backend
        I=widgets.interact_manual(self.BackEnd, R1=self.R1Slider,
                              R2=self.R2Slider)
        display(I)

    def BackEnd(self, R1, R2):
        """
        The backend wrapper to the TB and simulation
        """

        #create the Test Bench and Bind to this instance the NonInverting Topo
        TB=OnePortOpAmpTB(NonInvertingOpAmp)
        #Setup the Testbench; use a R1=20 Ohm,R2=5 Ohm and print the TB
        TB.SetupCir(**{'R1':R1*1e3, 'R2':R2*1e3})
        #Run the AC Bode Simulation
        TB.Simulate()
        #View Bode Simulation
        TB.PlotResults()

```

```python
NonInvertingExp();
```

    interactive(children=(FloatSlider(value=50.0, description='R1 kOhm', min=10.0, step=5.0), FloatSlider(value=50…

    <function ipywidgets.widgets.interaction._InteractFactory.__call__.<locals>.<lambda>>

# Miller Capactive Integrator

```python
class MillerCapIntOpAmp(SubCircuitFactory):
    """
    Inverting OpAmp SubCir
    Termanals:
        Vin
        Vout
    Parms:
        R1[Ohms]
        C1[Fards]
    """

    __name__='MillerCapIntOpAmp'
    __nodes__=('Vin','Vout')

    def __init__(self, R1=1, C1=1):
        super().__init__()
        self.R1=R1; self.C1=C1

        self.R('1', 'Vin', '2', R1@u_Ω)
        self.C('1', '2', 'Vout', C1@u_F)

        self.subcircuit(BasicOperationalAmplifier())
        self.X('op', 'BasicOperationalAmplifier', self.gnd, '2', 'Vout')
        self.Theory()

    @staticmethod
    def Theory():
        """Analog Electronics: Circuits, Systems and Signal Processing
        Crecraft Gergely"""
        R1, C1=symbols('R_1, C_1', real=True, postive=True)
        A,Zi, Zo, s=symbols('A, Z_i, Z_o s')
        H=Function('H')(s); Zin=Function('Z_in')(s); Zout=Function('Z_out')(s)
        TF=Eq(H, -(1/(s*C1))/R1)
        Zin=simplify(Eq(Zin, R1))
        Zout=Eq(Zout, Zo/(A(1+s*C1*R1)))
        return TF, Zin, Zout

```

```python
#Print out the MillerCapInt Op Amp Theory
MillerCapIntOpAmp.Theory()
```

```python
class MillerCapIntExp():
    """
    Class to provide GUI for  MillerCapInt
    """
    def __init__(self):
        """
        Front end and binding to backend
        """
        #create the GUI elments
        self.R1Slider=widgets.FloatSlider(value=50,
                                         min=10, max=100, step=5,
                                         description='R1 kOhm',
                                         readout_format='.1e')
        self.C1Slider=widgets.FloatSlider(value=50,
                                         min=1, max=500, step=25,
                                         description='C1 pF')
        #layout
        self.RSVBox=widgets.VBox([self.R1Slider, self.C1Slider])

        #bind to the backend
        I=widgets.interact_manual(self.BackEnd, R1=self.R1Slider,
                              C1=self.C1Slider)
        display(I)

    def BackEnd(self, R1, C1):
        """
        The backend wrapper to the TB and simulation
        """

        #create the Test Bench and Bind to this instance the MillerCapInt Topo
        TB=OnePortOpAmpTB(MillerCapIntOpAmp)
        #Setup the Testbench; use a R1=20 Ohm,C1=10 muF and print the TB
        TB.SetupCir(**{'R1':R1*1e3, 'C1':C1*1e-12})
        #Run the AC Bode Simulation
        TB.Simulate()
        #View Bode Simulation
        TB.PlotResults()
```

```python
MillerCapIntExp()

```

    interactive(children=(FloatSlider(value=50.0, description='R1 kOhm', min=10.0, readout_format='.1e', step=5.0)…

    <function ipywidgets.widgets.interaction._InteractFactory.__call__.<locals>.<lambda>>

    <__main__.MillerCapIntExp at 0x7fd2aeb03080>

# Miller Inductive Integrator

```python
class MillerIndIntOpAmp(SubCircuitFactory):
    """
    Inverting OpAmp SubCir
    Termanals:
        Vin
        Vout
    Parms:
        R1[Ohms]
        L1[Henrys]
    """

    __name__='MillerIndIntOpAmp'
    __nodes__=('Vin','Vout')

    def __init__(self, R1=1, L1=1):
        super().__init__()
        self.R1=R1; self.L1=L1

        self.L('1', 'Vin', '2', L1@u_H)
        self.R('1', '2', 'Vout', R1@u_Ω)

        self.subcircuit(BasicOperationalAmplifier())
        self.X('op', 'BasicOperationalAmplifier', self.gnd, '2', 'Vout')
        self.Theory()

    @staticmethod
    def Theory():
        """Analog Electronics: Circuits, Systems and Signal Processing
        Crecraft Gergely"""
        R1, L1=symbols('R_1, L_1', real=True, postive=True)
        A,Zi, Zo, s=symbols('A, Z_i, Z_o s')
        H=Function('H')(s); Zin=Function('Z_in')(s); Zout=Function('Z_out')(s)
        TF=Eq(H, -R1/(s*L1))
        Zin=Eq(Zin, s*L1)
        Zout=simplify(Eq(Zout, Zo*(s*L1+R1)/(A*s*L1)))
        return TF, Zin, Zout

```

```python
#Print out the MillerIndInt Op Amp Theory
MillerIndIntOpAmp.Theory()
```

```python
class MillerIndIntExp():
    """
    Class to provide GUI for  MillerIndInt
    """
    def __init__(self):
        """
        Front end and binding to backend
        """
        #create the GUI elments
        self.R1Slider=widgets.FloatSlider(value=50,
                                         min=10, max=100, step=5,
                                         description='R1 kOhm',
                                         readout_format='.1e')
        self.L1Slider=widgets.FloatSlider(value=50,
                                         min=1, max=500, step=25,
                                         description='L1 uH')
        #layout
        self.RSVBox=widgets.VBox([self.R1Slider, self.L1Slider])

        #bind to the backend
        I=widgets.interact_manual(self.BackEnd, R1=self.R1Slider,
                              L1=self.L1Slider)
        display(I)

    def BackEnd(self, R1, L1):
        """
        The backend wrapper to the TB and simulation
        """

        #create the Test Bench and Bind to this instance the MillerCapInt Topo
        TB=OnePortOpAmpTB(MillerIndIntOpAmp)
        #Setup the Testbench; use a R1=20 Ohm,L1=10 muH and print the TB
        TB.SetupCir(**{'R1':R1*1e3, 'L1':L1*1e-6})
        #Run the AC Bode Simulation
        TB.Simulate()
        #View Bode Simulation
        TB.PlotResults()
```

```python
MillerIndIntExp();
```

    interactive(children=(FloatSlider(value=50.0, description='R1 kOhm', min=10.0, readout_format='.1e', step=5.0)…

    <function ipywidgets.widgets.interaction._InteractFactory.__call__.<locals>.<lambda>>

#  Low Pass 1Pole

```python
class LowPass1POpAmp(SubCircuitFactory):
    """
    Single Pole Low Pass OpAmp SubCir

    Termanals:
        Vin
        Vout
    Parms:
        R1[Ohms]
        R2[Ohms]
        C1[Farads]
    """

    __name__='LowPass1POpAmp'
    __nodes__=('Vin','Vout')

    def __init__(self, R1=1, R2=1, C1=1):
        super().__init__()
        self.R1=R1; self.R2=R2; self.C1=C1

        self.R('1', 'Vin', '2', R1@u_Ω)
        self.R('2', '2', 'Vout', R2@u_Ω)
        self.C('1', '2', 'Vout', C1@u_F)

        self.subcircuit(BasicOperationalAmplifier())
        #non, inv, out
        self.X('op', 'BasicOperationalAmplifier', 'Vin', '2', 'Vout')

    @staticmethod
    def Theory():
        """Analog Electronics: Circuits, Systems and Signal Processing
        Crecraft Gergely"""
        R1, R2, C1=symbols('R_1, R_2, C_1', real=True, postive=True)
        A,Zi, Zo, s=symbols('A, Z_i, Z_o s')
        H=Function('H')(s); Zin=Function('Z_in')(s); Zout=Function('Z_out')(s)
        TF=simplify(Eq(H, -(R2/(R2*s*C1+1))/R1))
        Zin=Eq(Zin, R1)
        Zout=simplify(Eq(Zout, Zo*(R1+(R2/(R2*s*C1+1)))/(A*R1)))
        return TF, Zin, Zout

```

```python
LowPass1POpAmp.Theory()
```

```python
class LowPass1POpAmpExp():
    """
    Class to provide GUI for  LowPass1POpAmp
    """
    def __init__(self):
        """
        Front end and binding to backend
        """
        #create the GUI elments
        self.R1Slider=widgets.FloatSlider(value=50,
                                         min=10, max=100, step=5,
                                         description='R1 kOhm',
                                         readout_format='.1e')

        self.R2Slider=widgets.FloatSlider(value=50,
                                         min=10, max=100, step=5,
                                         description='R2 kOhm',
                                         readout_format='.1e')

        self.C1Slider=widgets.FloatSlider(value=50,
                                         min=1, max=500, step=25,
                                         description='C1 pF')
        #layout
        self.RSVBox=widgets.VBox([self.R1Slider, self.R2Slider, self.C1Slider])

        #bind to the backend
        I=widgets.interact_manual(self.BackEnd, R1=self.R1Slider,
                                  R2=self.R2Slider,
                              C1=self.C1Slider)
        display(I)

    def BackEnd(self, R1, R2, C1):
        """
        The backend wrapper to the TB and simulation
        """

        #create the Test Bench and Bind to this instance the LowPass1POpAmp Topo
        TB=OnePortOpAmpTB(LowPass1POpAmp)
        #Setup the Testbench; use a R1=20 Ohm, R2=5 Ohm,C1=10 muH and print the TB
        TB.SetupCir(**{'R1':R1*1e3, 'R2':R2*1e3, 'C1':C1*1e-12})
        #Run the AC Bode Simulation
        TB.Simulate()
        #View Bode Simulation
        TB.PlotResults()

```

```python
LowPass1POpAmpExp();
```

    interactive(children=(FloatSlider(value=50.0, description='R1 kOhm', min=10.0, readout_format='.1e', step=5.0)…

    <function ipywidgets.widgets.interaction._InteractFactory.__call__.<locals>.<lambda>>

# Miller Capacitive Differentiator

```python
class MillerCapDiff(SubCircuitFactory):
    """
    Miller Capacitive Differentiator  OpAmp SubCir
    Termanals:
        Vin
        Vout
    Parms:
        R1[Ohms]
        C1[Farads]
    """

    __name__='MillerCapDiff'
    __nodes__=('Vin','Vout')

    def __init__(self, R1=1, C1=1):
        super().__init__()
        self.R1=R1; self.C1=C1

        self.C('1', 'Vin', '2', C1@u_F)
        self.R('1', '2', 'Vout', R1@u_Ω)

        self.subcircuit(BasicOperationalAmplifier())
        self.X('op', 'BasicOperationalAmplifier', self.gnd, '2', 'Vout')
        self.Theory()

    @staticmethod
    def Theory():
        """Analog Electronics: Circuits, Systems and Signal Processing
        Crecraft Gergely"""
        R1, R2, C1=symbols('R_1, R_2, C_1', real=True, postive=True)
        A,Zi, Zo, s=symbols('A, Z_i, Z_o s')
        H=Function('H')(s); Zin=Function('Z_in')(s); Zout=Function('Z_out')(s)
        TF=Eq(H, -R1/(1/(s*C1)))
        Zin=Eq(Zin, (1/(s*C1)))
        Zout=simplify(Eq(Zout, Zo*((1/(s*C1))+R1)/(A*(1/(s*C1)))))
        return TF, Zin, Zout

```

```python
MillerCapDiff.Theory()
```

```python
class MillerCapDiffExp():
    """
    Class to provide GUI for  MillerCapDiff
    """
    def __init__(self):
        """
        Front end and binding to backend
        """
        #create the GUI elments
        self.R1Slider=widgets.FloatSlider(value=50,
                                         min=10, max=100, step=5,
                                         description='R1 kOhm',
                                         readout_format='.1e')
        self.C1Slider=widgets.FloatSlider(value=50,
                                         min=1, max=500, step=25,
                                         description='C1 pF')
        #layout
        self.RSVBox=widgets.VBox([self.R1Slider, self.C1Slider])

        #bind to the backend
        I=widgets.interact_manual(self.BackEnd, R1=self.R1Slider,
                              C1=self.C1Slider)
        display(I)

    def BackEnd(self, R1, C1):
        """
        The backend wrapper to the TB and simulation
        """

        #create the Test Bench and Bind to this instance the MillerCapDiff Topo
        TB=OnePortOpAmpTB(MillerCapDiff)
        #Setup the Testbench; use a R1=20 Ohm,C1=10 muF and print the TB
        TB.SetupCir(**{'R1':R1*1e3, 'C1':C1*1e-12})
        #Run the AC Bode Simulation
        TB.Simulate()
        #View Bode Simulation
        TB.PlotResults()
```

```python
MillerCapDiffExp();
```

    interactive(children=(FloatSlider(value=50.0, description='R1 kOhm', min=10.0, readout_format='.1e', step=5.0)…

    <function ipywidgets.widgets.interaction._InteractFactory.__call__.<locals>.<lambda>>

# Miller Inductive Differentiator

```python
class MillerIndDiff(SubCircuitFactory):
    """
    Miller Inductive Differentiator  OpAmp SubCir
    Termanals:
        Vin
        Vout
    Parms:
        R1[Ohms]
        L1[Henerys]
    """

    __name__='MillerIndDiff'
    __nodes__=('Vin','Vout')

    def __init__(self, R1=1, L1=1):
        super().__init__()
        self.R1=R1; self.L1=L1

        self.R('1', 'Vin', '2', R1@u_Ω)
        self.L('1', '2', 'Vout', L1@u_H)

        self.subcircuit(BasicOperationalAmplifier())
        self.X('op', 'BasicOperationalAmplifier', self.gnd, '2', 'Vout')
        self.Theory()

    @staticmethod
    def Theory():
        """Analog Electronics: Circuits, Systems and Signal Processing
        Crecraft Gergely"""
        R1, R2, L1=symbols('R_1, R_2, L_1', real=True, postive=True)
        A,Zi, Zo, s=symbols('A, Z_i, Z_o s')
        H=Function('H')(s); Zin=Function('Z_in')(s); Zout=Function('Z_out')(s)
        TF=Eq(H, -(s*L1)/R1)
        Zin=Eq(Zin, R1)
        Zout=Eq(Zout, Zo*(R1+(s*L1))/(A*R1))
        return TF, Zin, Zout

```

```python
MillerIndDiff.Theory()
```

```python
class MillerIndDiffExp():
    """
    Class to provide GUI for  MillerIndDiff
    """
    def __init__(self):
        """
        Front end and binding to backend
        """
        #create the GUI elments
        self.R1Slider=widgets.FloatSlider(value=50,
                                         min=10, max=100, step=5,
                                         description='R1 kOhm',
                                         readout_format='.1e')
        self.L1Slider=widgets.FloatSlider(value=50,
                                         min=1, max=500, step=25,
                                         description='L1 uH')
        #layout
        self.RSVBox=widgets.VBox([self.R1Slider, self.L1Slider])

        #bind to the backend
        I=widgets.interact_manual(self.BackEnd, R1=self.R1Slider,
                              L1=self.L1Slider)
        display(I)

    def BackEnd(self, R1, L1):
        """
        The backend wrapper to the TB and simulation
        """

        #create the Test Bench and Bind to this instance the MillerCapInt Topo
        TB=OnePortOpAmpTB(MillerIndDiff)
        #Setup the Testbench; use a R1=20 Ohm,L1=10 muH and print the TB
        TB.SetupCir(**{'R1':R1*1e3, 'L1':L1*1e-12})
        #Run the AC Bode Simulation
        TB.Simulate()
        #View Bode Simulation
        TB.PlotResults()
```

```python
MillerIndDiffExp();
```

    interactive(children=(FloatSlider(value=50.0, description='R1 kOhm', min=10.0, readout_format='.1e', step=5.0)…

    <function ipywidgets.widgets.interaction._InteractFactory.__call__.<locals>.<lambda>>

# High Pass 1Zero

```python
class HighPass1ZOpAmp(SubCircuitFactory):
    """
    Single Pole Low Pass OpAmp SubCir

    Termanals:
        Vin
        Vout
    Parms:
        R1[Ohms]
        R2[Ohms]
        L1[Henerys]
    """

    __name__='HighPass1ZOpAmp'
    __nodes__=('Vin','Vout')

    def __init__(self, R1=1, R2=1, L1=1):
        super().__init__()
        self.R1=R1; self.R2=R2; self.L1=L1

        self.R('1', 'Vin', '2', R1@u_Ω)
        self.R('2', '2', 'Vout', R2@u_Ω)
        self.L('1', '2', 'Vout', L1@u_H)

        self.subcircuit(BasicOperationalAmplifier())
        #non, inv, out
        self.X('op', 'BasicOperationalAmplifier', 'Vin', '2', 'Vout')

    @staticmethod
    def Theory():
        """Analog Electronics: Circuits, Systems and Signal Processing
        Crecraft Gergely"""
        R1, R2, L1=symbols('R_1, R_2, L_1', real=True, postive=True)
        A,Zi, Zo, s=symbols('A, Z_i, Z_o s')
        H=Function('H')(s); Zin=Function('Z_in')(s); Zout=Function('Z_out')(s)
        TF=simplify(Eq(H, -((R2*s*L1)/(s*L1+R2))/R1))
        Zin=Eq(Zin, R1)
        Zout=simplify(Eq(Zout, Zo*(R1+((R2*s*L1)/(s*L1+R2)))/(A*R1)))
        return TF, Zin, Zout

```

```python
HighPass1ZOpAmp.Theory()
```

```python
class HighPass1ZExp():
    """
    Class to provide GUI for  HighPass1ZOpAmp
    """
    def __init__(self):
        """
        Front end and binding to backend
        """
        #create the GUI elments
        self.R1Slider=widgets.FloatSlider(value=50,
                                         min=10, max=100, step=5,
                                         description='R1 kOhm',
                                         readout_format='.1e')

        self.R2Slider=widgets.FloatSlider(value=50,
                                         min=10, max=100, step=5,
                                         description='R2 kOhm',
                                         readout_format='.1e')

        self.L1Slider=widgets.FloatSlider(value=50,
                                         min=1, max=500, step=25,
                                         description='L1 uH')
        #layout
        self.RSVBox=widgets.VBox([self.R1Slider, self.R2Slider, self.L1Slider])

        #bind to the backend
        I=widgets.interact_manual(self.BackEnd, R1=self.R1Slider,
                                  R2=self.R2Slider,
                              L1=self.L1Slider)
        display(I)

    def BackEnd(self, R1, R2, L1):
        """
        The backend wrapper to the TB and simulation
        """

        #create the Test Bench and Bind to this instance the HighPass1ZOpAmp Topo
        TB=OnePortOpAmpTB(HighPass1ZOpAmp)
        #Setup the Testbench; use a R1=20 Ohm,R2=5 Ohm, L1=10 muH and print the TB
        TB.SetupCir(**{'R1':R1*1e3, 'R2':R2*1e3, 'L1':L1*1e-6})
        #Run the AC Bode Simulation
        TB.Simulate()
        #View Bode Simulation
        TB.PlotResults()

```

```python
HighPass1ZExp();
```

    interactive(children=(FloatSlider(value=50.0, description='R1 kOhm', min=10.0, readout_format='.1e', step=5.0)…

    <function ipywidgets.widgets.interaction._InteractFactory.__call__.<locals>.<lambda>>

# AllPass (Phase) Filter
http://www.analog.com/media/en/training-seminars/tutorials/MT-202.pdf

## First Order Cap First

```python
class AllPass1CapFOpAmp(SubCircuitFactory):
    """
    AllPass 1st Order Cap First
    Termanals:
        Vin
        Vout
    Parms:
        R1[Ohms]
        R2[Ohms]
        R3[Ohms]
        C1[Farads]

    """

    __name__='AllPass1CapFOpAmp'
    __nodes__=('Vin','Vout')

    def __init__(self, R1=1, R2=1, R3=1, C1=1):
        super().__init__()
        self.R1=R1; self.R2=R2; self.R3=R3
        self.C1=C1

        #inverting feedback stucter
        self.R('1', 'Vin', '2', R1@u_Ω)
        self.R('2', '2', 'Vout', R2@u_Ω)

        #Voltage Divider
        self.C('1', 'Vin', '3', C1@u_F)
        self.R('3', '3', self.gnd, R3@u_Ω)

        self.subcircuit(BasicOperationalAmplifier())
        self.X('op', 'BasicOperationalAmplifier', '3', '2', 'Vout')
        self.Theory()

    @staticmethod
    def Theory():
        A, R1, R2, R3, C1=symbols('A, R_1, R_2, R_3, C_1', real=True, postive=True)
        Vn, Vp, Vo, Vi=symbols('V_n, V_p, V_o, V_i')
        s=symbols('s')
        VpEq=Eq(Vp, (s*C1*R3)/(1+s*C1*R3)* Vi); VpEq
        VnEq=Eq(Vn, (R1/(R1+R2))*Vo+(R2/(R1+R2))*Vi); VnEq
        VoEq=Eq(Vo, A*(Vp-Vn)); VoEq
        VoEq=VoEq.subs({VpEq.lhs: VpEq.rhs, VnEq.lhs:VnEq.rhs})
        H=Function('H')(s)
        TF=Eq(H, (solve(VoEq, Vo)[0]/Vi)); TF
        return TF

```

```python
class AllPass1CapFExp():
    """
    Class to provide GUI for Inverting AllPass1CapF
    """
    def __init__(self):
        """
        Front end and binding to backend
        """
        #create the GUI elments
        self.R1Slider=widgets.FloatSlider(value=50,
                                         min=10, max=100, step=5,
                                         description='R1 kOhm',
                                         )
        self.R2Slider=widgets.FloatSlider(value=50,
                                         min=10, max=100, step=5,
                                         description='R2 kOhm',
                                         )

        self.R3Slider=widgets.FloatSlider(value=50,
                                         min=10, max=100, step=5,
                                         description='R2 kOhm',
                                         )

        self.C1Slider=widgets.FloatSlider(value=50,
                                         min=1, max=500, step=25,
                                         description='C1 pF')
        #layout
        self.RSVBox=widgets.VBox([self.R1Slider, self.R2Slider])

        #bind to the backend
        I=widgets.interact_manual(self.BackEnd, R1=self.R1Slider,
                              R2=self.R2Slider, R3=self.R3Slider, C1=self.C1Slider)
        display(I)

    def BackEnd(self, R1, R2, R3, C1):
        """
        The backend wrapper to the TB and simulation
        """

        #create the Test Bench and Bind to this instance the HighPass1ZOpAmp Topo
        TB=OnePortOpAmpTB(AllPass1CapFOpAmp)
        #Setup the Testbench; use a R1=20 Ohm,R2=5 Ohm, L1=10 muH and print the TB
        TB.SetupCir(**{'R1':R1*1e3, 'R2':R2*1e3, 'R3':R3*1e3, 'C1':C1*1e-9})
        #Run the AC Bode Simulation
        TB.Simulate()
        #View Bode Simulation
        TB.PlotResults()

```

```python
AllPass1CapFExp()
```

    interactive(children=(FloatSlider(value=50.0, description='R1 kOhm', min=10.0, step=5.0), FloatSlider(value=50…

    <function ipywidgets.widgets.interaction._InteractFactory.__call__.<locals>.<lambda>>

    <__main__.AllPass1CapFExp at 0x7fd2ae81c390>

## First Order Cap Second

```python
class AllPass1CapSOpAmp(SubCircuitFactory):
    """
    AllPass 1st Order Cap Second
    Termanals:
        Vin
        Vout
    Parms:
        R1[Ohms]
        R2[Ohms]
        R3[Ohms]
        C1[Farads]

    """

    __name__='AllPass1CapSOpAmp'
    __nodes__=('Vin','Vout')

    def __init__(self, R1=1, R2=1, R3=1, C1=1):
        super().__init__()
        self.R1=R1; self.R2=R2; self.R3=R3
        self.C1=C1

        #inverting feedback stucter
        self.R('1', 'Vin', '2', R1@u_Ω)
        self.R('2', '2', 'Vout', R2@u_Ω)

        #Voltage Divider
        self.R('3', 'Vin', '3', R3@u_Ω)
        self.C('1', '3', self.gnd, C1@u_F)

        self.subcircuit(BasicOperationalAmplifier())
        self.X('op', 'BasicOperationalAmplifier', '3', '2', 'Vout')
        self.Theory()

    @staticmethod
    def Theory():
        A, R1, R2, R3, C1=symbols('A, R_1, R_2, R_3, C_1', real=True, postive=True)
        Vn, Vp, Vo, Vi=symbols('V_n, V_p, V_o, V_i')
        s=symbols('s')
        VpEq=simplify(Eq(Vp, ((1/(s*C1))/((1/s*C1 +R3)))* Vi)); VpEq
        VnEq=Eq(Vn, (R1/(R1+R2))*Vo+(R2/(R1+R2))*Vi); VnEq
        VoEq=Eq(Vo, A*(Vp-Vn)); VoEq
        VoEq=VoEq.subs({VpEq.lhs: VpEq.rhs, VnEq.lhs:VnEq.rhs})
        H=Function('H')(s)
        TF=Eq(H, (solve(VoEq, Vo)[0]/Vi)); TF
        return TF

```

```python
AllPass1CapSOpAmp.Theory()
```

```python
class AllPass1CapSExp():
    """
    Class to provide GUI for Inverting AllPass1CapS
    """
    def __init__(self):
        """
        Front end and binding to backend
        """
        #create the GUI elments
        self.R1Slider=widgets.FloatSlider(value=50,
                                         min=10, max=100, step=5,
                                         description='R1 kOhm',
                                         )
        self.R2Slider=widgets.FloatSlider(value=50,
                                         min=10, max=100, step=5,
                                         description='R2 kOhm',
                                         )

        self.R3Slider=widgets.FloatSlider(value=50,
                                         min=10, max=100, step=5,
                                         description='R2 kOhm',
                                         )

        self.C1Slider=widgets.FloatSlider(value=50,
                                         min=1, max=500, step=25,
                                         description='C1 pF')
        #layout
        self.RSVBox=widgets.VBox([self.R1Slider, self.R2Slider])

        #bind to the backend
        I=widgets.interact_manual(self.BackEnd, R1=self.R1Slider,
                              R2=self.R2Slider, R3=self.R3Slider, C1=self.C1Slider)
        display(I)

    def BackEnd(self, R1, R2, R3, C1):
        """
        The backend wrapper to the TB and simulation
        """

        #create the Test Bench and Bind to this instance the HighPass1ZOpAmp Topo
        TB=OnePortOpAmpTB(AllPass1CapSOpAmp)
        #Setup the Testbench; use a R1=20 Ohm,R2=5 Ohm, L1=10 muH and print the TB
        TB.SetupCir(**{'R1':R1*1e3, 'R2':R2*1e3, 'R3':R3*1e3, 'C1':C1*1e-9})
        #Run the AC Bode Simulation
        TB.Simulate()
        #View Bode Simulation
        TB.PlotResults()

```

```python
AllPass1CapSExp()
```

    interactive(children=(FloatSlider(value=50.0, description='R1 kOhm', min=10.0, step=5.0), FloatSlider(value=50…

    <function ipywidgets.widgets.interaction._InteractFactory.__call__.<locals>.<lambda>>

    <__main__.AllPass1CapSExp at 0x7fd2ae7de780>

# Sallen-Key Filter
[Analog Electronics: Circuits, Systems and Signal Processing Crecraft & Gergely](https://www.amazon.com/Analog-Electronics-Circuits-Systems-Processing-ebook/dp/B00CXO975A/ref=sr_1_1?ie=UTF8&qid=1524885102&sr=8-1&keywords=Analog+Electronics+Crecraft+Gergely) 10.6.2

$$G=\dfrac{A_V Y_1 Y_3}{Y_5(Y_1+Y_2+Y_3+Y_4)+Y_3(Y_1+Y_2+Y_4(1+A_V))}$$

## Low Pass with Voltage Gain
$$Y_1=R_1$$
$$Y_2=0$$
$$Y_3=R_3$$
$$Y_4=sC_4$$
$$Y_5=sC_5$$

```python
class LPSKV(SubCircuitFactory):
    """
    Low Pass Sallen-Key with Voltage Gain NonInverting Amp

    Termanals:
        Vin
        Vout
    Parms:
        R1[Ohms]
        R3[Ohms]
        C4[Farads]
        C5[Farads]
        RF1[Ohms]: NonInverting Feedback R1
        RF2[Ohms]: NonInverting Feedback R2

    """

    __name__='LPSKV'
    __nodes__=('Vin','Vout')

    def __init__(self, R1=1, R3=1, C4=1e-12, C5=1e-12, RF1=1, RF2=1):
        super().__init__()
        self.R1=R1; self.R3=R3; self.C4=C4; self.C5=C5
        self.RF1=RF1; self.RF2=RF2

        self.R('1', 'Vin', '1', R1@u_Ω)
        self.R('3', '1', '2', R3@u_Ω)
        self.C('4', '1', 'Vout', C4@u_F)
        self.C('5', '2', self.gnd, C5@u_F)

        self.subcircuit(NonInvertingOpAmp(R1=RF1, R2=RF2))
        self.X('Av', 'NonInvertingOpAmp', '2', 'Vout')

```

```python
class LPSKVExp():
    """
    Class to provide GUI for  LPSKV
    """
    def __init__(self):
        """
        Front end and binding to backend
        """
        #create the GUI elments
        self.R1Slider=widgets.FloatSlider(value=50,
                                         min=10, max=100, step=5,
                                         description='R1 kOhm',
                                         readout_format='.1e')

        self.R3Slider=widgets.FloatSlider(value=50,
                                         min=10, max=100, step=5,
                                         description='R5 kOhm',
                                         readout_format='.1e')

        self.C4Slider=widgets.FloatSlider(value=50,
                                         min=1, max=500, step=25,
                                         description='C4 pF')

        self.C5Slider=widgets.FloatSlider(value=50,
                                         min=1, max=500, step=25,
                                         description='C5 pF')

        self.RF1Slider=widgets.FloatSlider(value=50,
                                         min=10, max=100, step=5,
                                         description='RF1 kOhm',
                                         readout_format='.1e')

        self.RF2Slider=widgets.FloatSlider(value=50,
                                         min=10, max=100, step=5,
                                         description='RF2 kOhm',
                                         readout_format='.1e')

        #layout
        self.RSVBox=widgets.VBox([self.R1Slider, self.R3Slider,
                                  self.C4Slider, self.C5Slider,
                                 self.RF1Slider, self.RF2Slider])

        #bind to the backend
        I=widgets.interact_manual(self.BackEnd,
                                  R1=self.R1Slider,
                                  R3=self.R3Slider,
                                  C4=self.C4Slider,
                                  C5=self.C5Slider,
                                  RF1=self.RF1Slider,
                                  RF2=self.RF2Slider)
        display(I)

    def BackEnd(self, R1, R3, C4, C5, RF1, RF2):
        """
        The backend wrapper to the TB and simulation
        """

        #create the Test Bench and Bind to this instance the LPSKV Topo
        TB=OnePortOpAmpTB(LPSKV)
        #Setup the Testbench; use a R1:20 Ohm,R2:5 Ohm, L1:10 muH and print the TB
        TB.SetupCir(**{'R1':R1*1e3, 'R3':R3*1e3, 'C4':C4*1e-12, 'C5':C5*1e-12,
                     'RF1':RF1*1e3, 'RF2':RF2*1e3})
        #Run the AC Bode Simulation
        TB.Simulate(fMax=10e9)
        #View Bode Simulation
        TB.PlotResults()

```

```python
LPSKVExp();
```

    interactive(children=(FloatSlider(value=50.0, description='R1 kOhm', min=10.0, readout_format='.1e', step=5.0)…

    <function ipywidgets.widgets.interaction._InteractFactory.__call__.<locals>.<lambda>>

## Band Pass with Voltage Gain
$$Y_1=R_1$$
$$Y_2=0$$
$$Y_3=C_3$$
$$Y_4=R_4$$
$$Y_5=sC_5$$

```python
class BPSKV(SubCircuitFactory):
    """
    Band Pass Sallen-Key with Voltage Gain NonInverting Amp

    Termanals:
        Vin
        Vout
    Parms:
        R1[Ohms]
        R4[Ohms]
        R5[Ohms]
        C3[Farads]
        C5[Farads]
        RF1[Ohms]: NonInverting Feedback R1
        RF2[Ohms]: NonInverting Feedback R2

    """

    __name__='BPSKV'
    __nodes__=('Vin','Vout')

    def __init__(self, R1=1, R4=1, R5=1, C3=1e-12, C5=1e-12, RF1=1, RF2=1):
        super().__init__()
        self.R1=R1; self.R4=R4; self.C3=C3; self.C5=C5
        self.RF1=RF1; self.RF2=RF2

        self.R('1', 'Vin', '1', R1@u_Ω)
        self.C('3', '1', '2', C3@u_F)
        self.R('4', '1', 'Vout', R4@u_Ω)
        self.C('5', '2', self.gnd, C5@u_F)
        self.R('5', '2', self.gnd, R5@u_Ω)

        self.subcircuit(NonInvertingOpAmp(R1=RF1, R2=RF2))
        self.X('Av', 'NonInvertingOpAmp', '2', 'Vout')

```

```python
class BPSKVExp():
    """
    Class to provide GUI for  BPSKV
    """
    def __init__(self):
        """
        Front end and binding to backend
        """
        #create the GUI elments
        self.R1Slider=widgets.FloatSlider(value=50,
                                         min=10, max=100, step=5,
                                         description='R1 kOhm',
                                         readout_format='.1e')

        self.R4Slider=widgets.FloatSlider(value=50,
                                         min=10, max=100, step=5,
                                         description='R4 kOhm',
                                         readout_format='.1e')
        self.R5Slider=widgets.FloatSlider(value=50,
                                         min=10, max=100, step=5,
                                         description='R5 kOhm',
                                         readout_format='.1e')

        self.C3Slider=widgets.FloatSlider(value=50,
                                         min=1, max=500, step=25,
                                         description='C3 pF')

        self.C5Slider=widgets.FloatSlider(value=50,
                                         min=1, max=500, step=5,
                                         description='C5 pF')

        self.RF1Slider=widgets.FloatSlider(value=50,
                                         min=10, max=100, step=5,
                                         description='RF1 kOhm',
                                         readout_format='.1e')

        self.RF2Slider=widgets.FloatSlider(value=50,
                                         min=10, max=100, step=5,
                                         description='RF2 kOhm',
                                         readout_format='.1e')

        #layout
        self.RSVBox=widgets.VBox([self.R1Slider, self.R4Slider, self.R5Slider,
                                  self.C3Slider, self.C5Slider,
                                 self.RF1Slider, self.RF2Slider])

        #bind to the backend
        I=widgets.interact_manual(self.BackEnd,
                                  R1=self.R1Slider,
                                  R4=self.R4Slider,
                                  R5=self.R5Slider,
                                  C3=self.C3Slider,
                                  C5=self.C5Slider,
                                  RF1=self.RF1Slider,
                                  RF2=self.RF2Slider)
        display(I)

    def BackEnd(self, R1, R4, R5, C3, C5, RF1, RF2):
        """
        The backend wrapper to the TB and simulation
        """

        #create the Test Bench and Bind to this instance the BPSKV Topo
        TB=OnePortOpAmpTB(BPSKV)
        #Setup the Testbench; use a R1:20 Ohm,R2:5 Ohm, L1:10 muH and print the TB
        TB.SetupCir(**{'R1':R1*1e3, 'R4':1e3, 'R5':1e3,
                       'C3':C3*1e-12, 'C5':C5*1e-12,
                     'RF1':RF1*1e3, 'RF2':RF2*1e3})
        #Run the AC Bode Simulation
        TB.Simulate(fMax=10e9)
        #View Bode Simulation
        TB.PlotResults()

```

```python
BPSKVExp();
```

    interactive(children=(FloatSlider(value=50.0, description='R1 kOhm', min=10.0, readout_format='.1e', step=5.0)…

    <function ipywidgets.widgets.interaction._InteractFactory.__call__.<locals>.<lambda>>

## HIgh Pass with Voltage Gain
$$Y_1=sC_1$$
$$Y_2=0$$
$$Y_3=sC_3$$
$$Y_4=R_4$$
$$Y_5=R_5$$

```python
class HPSKV(SubCircuitFactory):
    """
    High Pass Sallen-Key with Voltage Gain NonInverting Amp

    Termanals:
        Vin
        Vout
    Parms:
        R4[Ohms]
        R5[Ohms]
        C1[Farads]
        C3[Farads]
        RF1[Ohms]: NonInverting Feedback R1
        RF2[Ohms]: NonInverting Feedback R2

    """

    __name__='HPSKV'
    __nodes__=('Vin','Vout')

    def __init__(self, R4=1, R5=1, C1=1e-12, C3=1e-12, RF1=1, RF2=1):
        super().__init__()
        self.R4=R4; self.R5=R5; self.C1=C1; self.C3=C3
        self.RF1=RF1; self.RF2=RF2

        self.C('1', 'Vin', '1', C1@u_F)
        self.C('3', '1', '2', C3@u_F)
        self.R('4', '1', 'Vout', R4@u_Ω)
        self.R('5', '2', self.gnd, R5@u_Ω)

        self.subcircuit(NonInvertingOpAmp(R1=RF1, R2=RF2))
        self.X('Av', 'NonInvertingOpAmp', '2', 'Vout')

```

```python
class HPSKVExp():
    """
    Class to provide GUI for  HPSKV
    """
    def __init__(self):
        """
        Front end and binding to backend
        """
        #create the GUI elments
        self.R4Slider=widgets.FloatSlider(value=50,
                                         min=10, max=100, step=5,
                                         description='R4 kOhm',
                                         readout_format='.1e')

        self.R5Slider=widgets.FloatSlider(value=50,
                                         min=10, max=100, step=5,
                                         description='R5 kOhm',
                                         readout_format='.1e')

        self.C1Slider=widgets.FloatSlider(value=50,
                                         min=1, max=500, step=25,
                                         description='C1 pF')

        self.C3Slider=widgets.FloatSlider(value=50,
                                         min=1, max=500, step=5,
                                         description='C5 pF')

        self.RF1Slider=widgets.FloatSlider(value=50,
                                         min=10, max=100, step=5,
                                         description='RF1 kOhm',
                                         readout_format='.1e')

        self.RF2Slider=widgets.FloatSlider(value=50,
                                         min=10, max=100, step=5,
                                         description='RF2 kOhm',
                                         readout_format='.1e')

        #layout
        self.RSVBox=widgets.VBox([self.R4Slider, self.R5Slider,
                                  self.C1Slider, self.C3Slider,
                                 self.RF1Slider, self.RF2Slider])

        #bind to the backend
        I=widgets.interact_manual(self.BackEnd,
                                  R4=self.R4Slider,
                                  R5=self.R5Slider,
                                  C1=self.C1Slider,
                                  C3=self.C3Slider,
                                  RF1=self.RF1Slider,
                                  RF2=self.RF2Slider)

        display(I)

    def BackEnd(self, R4, R5, C1, C3, RF1, RF2):
        """
        The backend wrapper to the TB and simulation
        """

        #create the Test Bench and Bind to this instance the LPSKV Topo
        TB=OnePortOpAmpTB(HPSKV)
        #Setup the Testbench; use a R1:20 Ohm,R2:5 Ohm, L1:10 muH and print the TB
        TB.SetupCir(**{'R4':R4*1e3, 'R5':R5*1e3, 'C1':C1*1e-12, 'C3':C3*1e-12,
                     'RF1':RF1*1e3, 'RF2':RF2*1e3})
        #Run the AC Bode Simulation
        TB.Simulate(fMax=10e9)
        #View Bode Simulation
        TB.PlotResults()
```

```python
HPSKVExp();
```

    interactive(children=(FloatSlider(value=50.0, description='R4 kOhm', min=10.0, readout_format='.1e', step=5.0)…

    <function ipywidgets.widgets.interaction._InteractFactory.__call__.<locals>.<lambda>>

```python

```


```python
!pip install qiskit
```

    Collecting qiskit
      Downloading qiskit-0.32.1.tar.gz (13 kB)
    Collecting qiskit-terra==0.18.3
      Downloading qiskit_terra-0.18.3-cp37-cp37m-manylinux2010_x86_64.whl (6.1 MB)
         |████████████████████████████████| 6.1 MB 7.2 MB/s
    Collecting qiskit-aer==0.9.1
      Downloading qiskit_aer-0.9.1-cp37-cp37m-manylinux_2_12_x86_64.manylinux2010_x86_64.whl (17.9 MB)
         |████████████████████████████████| 17.9 MB 499 kB/s
    Collecting qiskit-ibmq-provider==0.18.1
      Downloading qiskit_ibmq_provider-0.18.1-py3-none-any.whl (237 kB)
         |████████████████████████████████| 237 kB 55.2 MB/s
    Collecting qiskit-ignis==0.6.0
      Downloading qiskit_ignis-0.6.0-py3-none-any.whl (207 kB)
         |████████████████████████████████| 207 kB 44.5 MB/s
    Collecting qiskit-aqua==0.9.5
      Downloading qiskit_aqua-0.9.5-py3-none-any.whl (2.1 MB)
         |████████████████████████████████| 2.1 MB 51.3 MB/s
    Requirement already satisfied: numpy>=1.16.3 in /usr/local/lib/python3.7/dist-packages (from qiskit-aer==0.9.1->qiskit) (1.19.5)
    Requirement already satisfied: scipy>=1.0 in /usr/local/lib/python3.7/dist-packages (from qiskit-aer==0.9.1->qiskit) (1.4.1)
    Requirement already satisfied: psutil>=5 in /usr/local/lib/python3.7/dist-packages (from qiskit-aqua==0.9.5->qiskit) (5.4.8)
    Collecting dlx<=1.0.4
      Downloading dlx-1.0.4.tar.gz (5.5 kB)
    Requirement already satisfied: h5py<3.3.0 in /usr/local/lib/python3.7/dist-packages (from qiskit-aqua==0.9.5->qiskit) (3.1.0)
    Collecting quandl
      Downloading Quandl-3.7.0-py2.py3-none-any.whl (26 kB)
    Requirement already satisfied: sympy>=1.3 in /usr/local/lib/python3.7/dist-packages (from qiskit-aqua==0.9.5->qiskit) (1.7.1)
    Collecting docplex>=2.21.207
      Downloading docplex-2.22.213.tar.gz (634 kB)
         |████████████████████████████████| 634 kB 58.9 MB/s
    Requirement already satisfied: fastdtw<=0.3.4 in /usr/local/lib/python3.7/dist-packages (from qiskit-aqua==0.9.5->qiskit) (0.3.4)
    Requirement already satisfied: pandas in /usr/local/lib/python3.7/dist-packages (from qiskit-aqua==0.9.5->qiskit) (1.1.5)
    Requirement already satisfied: setuptools>=40.1.0 in /usr/local/lib/python3.7/dist-packages (from qiskit-aqua==0.9.5->qiskit) (57.4.0)
    Collecting yfinance>=0.1.62
      Downloading yfinance-0.1.67-py2.py3-none-any.whl (25 kB)
    Collecting retworkx>=0.8.0
      Downloading retworkx-0.10.2-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl (1.4 MB)
         |████████████████████████████████| 1.4 MB 39.8 MB/s
    Requirement already satisfied: scikit-learn>=0.20.0 in /usr/local/lib/python3.7/dist-packages (from qiskit-aqua==0.9.5->qiskit) (1.0.1)
    Requirement already satisfied: requests>=2.19 in /usr/local/lib/python3.7/dist-packages (from qiskit-ibmq-provider==0.18.1->qiskit) (2.23.0)
    Collecting websocket-client>=1.0.1
      Downloading websocket_client-1.2.1-py2.py3-none-any.whl (52 kB)
         |████████████████████████████████| 52 kB 1.4 MB/s
    Requirement already satisfied: urllib3>=1.21.1 in /usr/local/lib/python3.7/dist-packages (from qiskit-ibmq-provider==0.18.1->qiskit) (1.24.3)
    Requirement already satisfied: python-dateutil>=2.8.0 in /usr/local/lib/python3.7/dist-packages (from qiskit-ibmq-provider==0.18.1->qiskit) (2.8.2)
    Collecting requests-ntlm>=1.1.0
      Downloading requests_ntlm-1.1.0-py2.py3-none-any.whl (5.7 kB)
    Collecting ply>=3.10
      Downloading ply-3.11-py2.py3-none-any.whl (49 kB)
         |████████████████████████████████| 49 kB 5.5 MB/s
    Collecting python-constraint>=1.4
      Downloading python-constraint-1.4.0.tar.bz2 (18 kB)
    Collecting symengine>0.7
      Downloading symengine-0.8.1-cp37-cp37m-manylinux2010_x86_64.whl (38.2 MB)
         |████████████████████████████████| 38.2 MB 1.5 MB/s
    Requirement already satisfied: jsonschema>=2.6 in /usr/local/lib/python3.7/dist-packages (from qiskit-terra==0.18.3->qiskit) (2.6.0)
    Requirement already satisfied: dill>=0.3 in /usr/local/lib/python3.7/dist-packages (from qiskit-terra==0.18.3->qiskit) (0.3.4)
    Collecting tweedledum<2.0,>=1.1
      Downloading tweedledum-1.1.1-cp37-cp37m-manylinux_2_12_x86_64.manylinux2010_x86_64.whl (943 kB)
         |████████████████████████████████| 943 kB 42.5 MB/s
    Collecting fastjsonschema>=2.10
      Downloading fastjsonschema-2.15.1-py3-none-any.whl (21 kB)
    Requirement already satisfied: six in /usr/local/lib/python3.7/dist-packages (from docplex>=2.21.207->qiskit-aqua==0.9.5->qiskit) (1.15.0)
    Requirement already satisfied: cached-property in /usr/local/lib/python3.7/dist-packages (from h5py<3.3.0->qiskit-aqua==0.9.5->qiskit) (1.5.2)
    Requirement already satisfied: chardet<4,>=3.0.2 in /usr/local/lib/python3.7/dist-packages (from requests>=2.19->qiskit-ibmq-provider==0.18.1->qiskit) (3.0.4)
    Requirement already satisfied: certifi>=2017.4.17 in /usr/local/lib/python3.7/dist-packages (from requests>=2.19->qiskit-ibmq-provider==0.18.1->qiskit) (2021.10.8)
    Requirement already satisfied: idna<3,>=2.5 in /usr/local/lib/python3.7/dist-packages (from requests>=2.19->qiskit-ibmq-provider==0.18.1->qiskit) (2.10)
    Collecting ntlm-auth>=1.0.2
      Downloading ntlm_auth-1.5.0-py2.py3-none-any.whl (29 kB)
    Collecting cryptography>=1.3
      Downloading cryptography-36.0.0-cp36-abi3-manylinux_2_24_x86_64.whl (3.6 MB)
         |████████████████████████████████| 3.6 MB 30.6 MB/s
    Requirement already satisfied: cffi>=1.12 in /usr/local/lib/python3.7/dist-packages (from cryptography>=1.3->requests-ntlm>=1.1.0->qiskit-ibmq-provider==0.18.1->qiskit) (1.15.0)
    Requirement already satisfied: pycparser in /usr/local/lib/python3.7/dist-packages (from cffi>=1.12->cryptography>=1.3->requests-ntlm>=1.1.0->qiskit-ibmq-provider==0.18.1->qiskit) (2.21)
    Requirement already satisfied: threadpoolctl>=2.0.0 in /usr/local/lib/python3.7/dist-packages (from scikit-learn>=0.20.0->qiskit-aqua==0.9.5->qiskit) (3.0.0)
    Requirement already satisfied: joblib>=0.11 in /usr/local/lib/python3.7/dist-packages (from scikit-learn>=0.20.0->qiskit-aqua==0.9.5->qiskit) (1.1.0)
    Requirement already satisfied: mpmath>=0.19 in /usr/local/lib/python3.7/dist-packages (from sympy>=1.3->qiskit-aqua==0.9.5->qiskit) (1.2.1)
    Requirement already satisfied: multitasking>=0.0.7 in /usr/local/lib/python3.7/dist-packages (from yfinance>=0.1.62->qiskit-aqua==0.9.5->qiskit) (0.0.10)
    Collecting lxml>=4.5.1
      Downloading lxml-4.6.4-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl (6.3 MB)
         |████████████████████████████████| 6.3 MB 51.8 MB/s
    Requirement already satisfied: pytz>=2017.2 in /usr/local/lib/python3.7/dist-packages (from pandas->qiskit-aqua==0.9.5->qiskit) (2018.9)
    Requirement already satisfied: more-itertools in /usr/local/lib/python3.7/dist-packages (from quandl->qiskit-aqua==0.9.5->qiskit) (8.12.0)
    Collecting inflection>=0.3.1
      Downloading inflection-0.5.1-py2.py3-none-any.whl (9.5 kB)
    Building wheels for collected packages: qiskit, dlx, docplex, python-constraint
      Building wheel for qiskit (setup.py) ... done
      Created wheel for qiskit: filename=qiskit-0.32.1-py3-none-any.whl size=11776 sha256=d70f4e50b8ab84eabd662f4b9a91b2da056029717bf5d4672a9d38da31a1e61e
      Stored in directory: /root/.cache/pip/wheels/0f/62/0a/c53eda1ead41c137c47c9730bc2771a8367b1ce00fb64e8cc6
      Building wheel for dlx (setup.py) ... done
      Created wheel for dlx: filename=dlx-1.0.4-py3-none-any.whl size=5719 sha256=67da71313e4dbc4de7d40821ebae9938e2bb87e38fa77d9242a315f1f9f14cb0
      Stored in directory: /root/.cache/pip/wheels/78/55/c8/dc61e772445a566b7608a476d151e9dcaf4e092b01b0c4bc3c
      Building wheel for docplex (setup.py) ... done
      Created wheel for docplex: filename=docplex-2.22.213-py3-none-any.whl size=696881 sha256=f9c11b932c15395edb8b66d2351d66a236b426bdb6a92100470985da782abeb4
      Stored in directory: /root/.cache/pip/wheels/90/69/6b/1375c68a5b7ff94c40263b151c86f58bd72200bf0c465b5ba3
      Building wheel for python-constraint (setup.py) ... done
      Created wheel for python-constraint: filename=python_constraint-1.4.0-py2.py3-none-any.whl size=24081 sha256=b24f4fa51b0e95be6df0a4b04468887c316e727be808b40c8602cf56edf6fe80
      Stored in directory: /root/.cache/pip/wheels/07/27/db/1222c80eb1e431f3d2199c12569cb1cac60f562a451fe30479
    Successfully built qiskit dlx docplex python-constraint
    Installing collected packages: tweedledum, symengine, retworkx, python-constraint, ply, fastjsonschema, qiskit-terra, ntlm-auth, lxml, inflection, cryptography, yfinance, websocket-client, requests-ntlm, quandl, qiskit-ignis, docplex, dlx, qiskit-ibmq-provider, qiskit-aqua, qiskit-aer, qiskit
      Attempting uninstall: lxml
        Found existing installation: lxml 4.2.6
        Uninstalling lxml-4.2.6:
          Successfully uninstalled lxml-4.2.6
    Successfully installed cryptography-36.0.0 dlx-1.0.4 docplex-2.22.213 fastjsonschema-2.15.1 inflection-0.5.1 lxml-4.6.4 ntlm-auth-1.5.0 ply-3.11 python-constraint-1.4.0 qiskit-0.32.1 qiskit-aer-0.9.1 qiskit-aqua-0.9.5 qiskit-ibmq-provider-0.18.1 qiskit-ignis-0.6.0 qiskit-terra-0.18.3 quandl-3.7.0 requests-ntlm-1.1.0 retworkx-0.10.2 symengine-0.8.1 tweedledum-1.1.1 websocket-client-1.2.1 yfinance-0.1.67

```python
#from IPython.core.debugger import set_trace
import numpy as np

# Importing standard Qiskit libraries
from qiskit import QuantumCircuit, transpile, Aer, IBMQ
from qiskit.tools.jupyter import *
from qiskit.visualization import *
#from ibm_quantum_widgets import *
from qiskit.providers.aer import QasmSimulator

# Loading your IBM Quantum account(s)
#provider = IBMQ.load_account()
```

```python
#Custom library
from __future__ import print_function

import os
import sys
#sys.path.append("../Qiskit_Dynamic_Modelling")
#sys.path.append("./modelQ")
#sys.path.append("./dynamicsQ")
sys.path.append("./sample_data")

from visualize import *
from ode_solver import *
#from network import *
from dataloader import *
from lagrangian import *

import matplotlib.pyplot as plt
%matplotlib inline

import torch
import torch.utils.data
from torch.autograd import Function
from torchvision import datasets, transforms
import torch.optim as optim
import torch.nn as nn
import torch.nn.functional as F
#from torchsummary import summary

from itertools import combinations
import numpy as np
import matplotlib.pyplot as plt

import qiskit
from qiskit.circuit.random import random_circuit
from qiskit import transpile, assemble
```

```python
print("--- Loading training and test data... ---")
train_data = np.load('/content/sample_data/train_dataset.npz')
train_inputs = train_data["input"]
train_labels = train_data["labels"]

X_train = train_inputs[:100]
y_train = train_labels[:100]

#train_dataset = DynamicsDataset(train_inputs, train_labels)
train_dataset = DynamicsDataset(X_train, y_train)

train_dataloader = torch.utils.data.DataLoader(train_dataset,
                                                 batch_size=1,
                                                 shuffle=True,
                                                 collate_fn=DynamicsDataset.collate_fn,
                                                 pin_memory=True,
                                                 num_workers=1)
test_data = np.load('/content/sample_data/val_dataset.npz')
test_inputs = train_data["input"]
test_labels = train_data["labels"]
X_test = test_inputs[:100]
y_test = test_labels[:100]

test_dataset = DynamicsDataset(X_test, y_test)

test_dataloader = torch.utils.data.DataLoader(test_dataset,
                                                 batch_size=1,
                                                 shuffle=False,
                                                 collate_fn=DynamicsDataset.collate_fn,
                                                 pin_memory=True,
                                                 num_workers=1)
print("--- Loading training and test data completed ---")
```

    --- Loading training and test data... ---
    --- Loading training and test data completed ---

```python
for (X_train, y_train) in train_dataloader:
    print('X_train:', X_train.size(), 'type:', X_train.type())
    print('y_train:', y_train.size(), 'type:', y_train.type())
    print('X_train:', X_train)
    print('y_train:', y_train)
    break

for (X_test, y_test) in test_dataloader:
    print('X_test:', X_test.size(), 'type:', X_test.type())
    print('y_test:', y_test.size(), 'type:', y_test.type())
    print('X_test:', X_test)
    print('y_test:', y_test)
    break
```

    ./sample_data/dataloader.py:35: UserWarning: Creating a tensor from a list of numpy.ndarrays is extremely slow. Please consider converting the list to a single numpy.ndarray with numpy.array() before converting to a tensor. (Triggered internally at  ../torch/csrc/utils/tensor_new.cpp:201.)
      batch_x = torch.as_tensor(batch_x)

    X_train: torch.Size([1, 4]) type: torch.DoubleTensor
    y_train: torch.Size([1, 4]) type: torch.DoubleTensor
    X_train: tensor([[ 3.0396,  2.1699,  0.1009, -0.1473]], dtype=torch.float64)
    y_train: tensor([[ 0.1009, -0.1473,  2.0250, -9.3997]], dtype=torch.float64)

    ./sample_data/dataloader.py:35: UserWarning: Creating a tensor from a list of numpy.ndarrays is extremely slow. Please consider converting the list to a single numpy.ndarray with numpy.array() before converting to a tensor. (Triggered internally at  ../torch/csrc/utils/tensor_new.cpp:201.)
      batch_x = torch.as_tensor(batch_x)

    X_test: torch.Size([1, 4]) type: torch.DoubleTensor
    y_test: torch.Size([1, 4]) type: torch.DoubleTensor
    X_test: tensor([[ 1.9537,  0.2797, -0.0780, -0.2268]], dtype=torch.float64)
    y_test: tensor([[-0.0780, -0.2268, -9.3138, -3.6618]], dtype=torch.float64)

```python
class QuantumCircuit:
    """
    This class provides a simple interface for interaction
    with the quantum circuit
    """

    def __init__(self, n_qubits, backend, shots):
        # --- Circuit definition ---
        self._circuit = qiskit.QuantumCircuit(n_qubits)

        all_qubits = [i for i in range(n_qubits)]
        self.theta = qiskit.circuit.Parameter('theta')

        self._circuit.h(all_qubits)
        self._circuit.barrier()
        self._circuit.ry(self.theta, all_qubits)

        self._circuit.measure_all()
        # ---------------------------

        self.backend = backend
        self.shots = shots

    def run(self, thetas):
        #set_trace()
        #print('cRun:',thetas)
        List = [thetas]
        t_qc = transpile(self._circuit,
                         self.backend)
        qobj = assemble(t_qc,
                        shots=self.shots,
                        parameter_binds = [{self.theta: theta} for theta in List])
        #set_trace()
        job = self.backend.run(qobj)
        result = job.result().get_counts()

        counts = np.array(list(result.values()))
        states = np.array(list(result.keys())).astype(float)

        # Compute probabilities for each state
        probabilities = counts / self.shots
        # Get state expectation
        expectation = np.sum(states * probabilities)

        return np.array([expectation])
```

```python
simulator = qiskit.Aer.get_backend('aer_simulator')

circuit = QuantumCircuit(1, simulator, 100)
#data = [2.1757166385650635]
print('Expected value for rotation pi {}'.format(circuit.run(2.1757166385650635)))
#print('Expected value for rotation pi {}'.format(circuit.run([1.4173])))
circuit._circuit.draw()
```

    /usr/local/lib/python3.7/dist-packages/qiskit/utils/deprecation.py:62: DeprecationWarning: Using a qobj for run() is deprecated as of qiskit-aer 0.9.0 and will be removed no sooner than 3 months from that release date. Transpiled circuits should now be passed directly using `backend.run(circuits, **run_options).
      return func(*args, **kwargs)

    Expected value for rotation pi [0.87]

<pre style="word-wrap: normal;white-space: pre;background: #fff0;line-height: 1.1;font-family: "Courier New",Courier,monospace">        ┌───┐ ░ ┌───────────┐ ░ ┌─┐
   q_0: ┤ H ├─░─┤ Ry(theta) ├─░─┤M├
        └───┘ ░ └───────────┘ ░ └╥┘
meas: 1/═════════════════════════╩═
                                 0 </pre>

```python
class HybridFunction(Function):
    """ Hybrid quantum - classical function definition """

    @staticmethod
    def forward(ctx, input, quantum_circuit, shift):
        """ Forward pass computation """
        ##print('hFor:', input)
        #print('hFor1:', input[0])
        #print('hFor2:', input[0].tolist())
        ctx.shift = shift
        ctx.quantum_circuit = quantum_circuit
        #expectation_z = []
        #for i in range(len(input)):
        #    expectation_z.append(ctx.quantum_circuit.run(input[i].tolist()))
        #print('expectation_z',expectation_z)
        expectation_z = ctx.quantum_circuit.run(input[0].tolist())
        result = torch.tensor([expectation_z])
        #result = torch.tensor(expectation_z)
        ctx.save_for_backward(input, result)

        return result

    @staticmethod
    def backward(ctx, grad_output):
        """ Backward pass computation """
        input, expectation_z = ctx.saved_tensors
        input_list = np.array(input.tolist())

        shift_right = input_list + np.ones(input_list.shape) * ctx.shift
        shift_left = input_list - np.ones(input_list.shape) * ctx.shift

        gradients = []
        for i in range(len(input_list)):
            expectation_right = ctx.quantum_circuit.run(shift_right[i])
            expectation_left  = ctx.quantum_circuit.run(shift_left[i])

            gradient = torch.tensor([expectation_right]) - torch.tensor([expectation_left])
            gradients.append(gradient)
        gradients = np.array([gradients]).T
        return torch.tensor([gradients]).float() * grad_output.float(), None, None

class Hybrid(nn.Module):
    """ Hybrid quantum - classical layer definition """

    def __init__(self, backend, shots, shift):
        super(Hybrid, self).__init__()
        self.quantum_circuit = QuantumCircuit(1, backend, shots)
        self.shift = shift

    def forward(self, input):
        #print('3',input)
        return HybridFunction.apply(input, self.quantum_circuit, self.shift)
```

```python
class LagrangianNeuralNetwork(torch.nn.Module):
    def __init__(self, D_in, hidden_list, D_out):
        """
        Neural Network used to approximate a paramaterized system lagrangian
        """
        super(LagrangianNeuralNetwork, self).__init__()
        self.model_layers = torch.nn.ModuleList()

        # input layer
        self.model_layers.append(torch.nn.Linear(D_in, hidden_list[0]))
        # self.model_layers.append(torch.nn.BatchNorm1d(hidden_list[0]))
        self.model_layers.append(torch.nn.Softplus())
        #self.model_layers.append(Hybrid(qiskit.Aer.get_backend('aer_simulator'), 100, np.pi / 2))
        # add all hiden layers
        for i in range(1, len(hidden_list)):
            self.model_layers.append(torch.nn.Linear(hidden_list[i-1], hidden_list[i]))
            # self.model_layers.append(torch.nn.BatchNorm1d(hidden_list[i]))
            self.model_layers.append(torch.nn.Softplus())

        self.hybrid = Hybrid(qiskit.Aer.get_backend('aer_simulator'), 100, np.pi / 2)
        #self.model_layers.append(Hybrid(qiskit.Aer.get_backend('aer_simulator'), 100, np.pi / 2))
        # output layer
        self.model_layers.append(torch.nn.Linear(hidden_list[-1], D_out))
        self.model_layers.append(torch.nn.Softplus())

    def forward(self, x):
        """
        applies all of the model layers, and returns the single output value,
        which in this case is the lagrangian of the system, representing the
        total energy
        """
        count = 0
        #print("2 ", x)
        for layer in self.model_layers:
            #print(len(self.model_layers))
            #if(count >= (len(self.model_layers) - 3)):
                #print(count)
                #print(x)
            #    x = self.hybrid(x)
                #print(x)
            x = layer(x)
            #print(count)
            #count = count + 1
            #x = self.hybrid(x)
        #print('c', x)
        x = self.hybrid(x)
        #print('q', x)
        return x
```

```python
# determine device
print("--- Checking for CUDA Device... ---")
use_cuda = torch.cuda.is_available()
device = torch.device("cuda:0" if use_cuda else "cpu")
```

    --- Checking for CUDA Device... ---

```python
# organize data
input_size = train_inputs.shape[1]
output_size = 1  # for all lagrangian systems, output should be just a scalar energy value

# build model
print("--- Constructing Model... ---")
D_in = input_size  # state size
# hidden_list = [D_in, 256, 256, 256, 256, 256]
hidden_list = [D_in, 32, 64, 128, 256, 512, 256, 128, 64, 32]
D_out = output_size
lnn_model = LagrangianNeuralNetwork(D_in, hidden_list, D_out)
#summary(lnn_model, (1, 28, 28), device='cpu')
# set up training parameters
learning_rate = 1e-4
weight_decay = 1e-5
momentum = 0.9
num_epochs = 20
optimizer = torch.optim.Adam(lnn_model.parameters(),
                             lr=learning_rate,
                             weight_decay=weight_decay)

if os.path.isfile("model_weights.pth"):
    print("Re-loading existing weights!")
    checkpoint = torch.load("model_weights.pth")
    lnn_model.load_state_dict(checkpoint['model_state_dict'])
    optimizer.load_state_dict(checkpoint['optimizer_state_dict'])

# ensure model is in train mode so gradients are properly calculated
lnn_model.train()
# load device to either GPU or CPU depending on hardware
lnn_model.to(device)

# set up loss function
loss_fcn = torch.nn.MSELoss()

# set up GradScaler to improve run speed
scaler = torch.cuda.amp.GradScaler()
```

    --- Constructing Model... ---

    /usr/local/lib/python3.7/dist-packages/torch/cuda/amp/grad_scaler.py:115: UserWarning: torch.cuda.amp.GradScaler is enabled, but CUDA is not available.  Disabling.
      warnings.warn("torch.cuda.amp.GradScaler is enabled, but CUDA is not available.  Disabling.")

```python
import time
print("--- Beginning Training! ---")
loss_listQCQ = []
lnn_model.train()
startTimeQCQ = time.time()
print('Training Start Time (in sec) : ',(startTimeQCQ))
print('Training Start Time : ',(time.asctime( time.localtime(time.time()) )))
for epoch in range(num_epochs):
    total_lossQCQ = []
    for batch_idx, (data, target) in enumerate(train_dataloader):
        data, target = data.to(device), target.to(device)
        optimizer.zero_grad()
        for p in lnn_model.parameters(): p.grad = None
        # Forward pass
        #output = model(data)
        # Calculating loss
        #loss = loss_func(output, target)
        # output from model is the energy calculated from the parameterized lagrangian
        #print('1',data)
        #print('1',data.detach().numpy())
        #for val in enumerate(data.detach().numpy()):
        #    print(val)
        Listk = []
        for val in data.detach().numpy():
            #print(val)
            for i in range(len(val)):
                #print(val[i])
                qcr = circuit.run(val[i])
                #print(qcr[0])
                Listk.append(qcr[0])
                #print(Listk)

        ListTarget = []
        #print(target)
        for valT in target.detach().numpy():
            #print(val)
            for j in range(len(valT)):
                #print(val[i])
                qcrT = circuit.run(valT[j])
                #print(qcr[0])
                ListTarget.append(qcrT[0])
                #print(ListTarget)
        targetData = torch.tensor(ListTarget)
        data = torch.squeeze(torch.tensor(Listk))
        #data = torch.squeeze(data)
        with torch.cuda.amp.autocast():
            target_pred = solve_euler_lagrange(lnn_model.forward, data.float())
            #print('1',target_pred)
            #print('2',target.float())
            loss = loss_fcn(target_pred.unsqueeze(0), targetData.float())
        # Backward pass
        scaler.scale(loss).backward()
        # Optimize the weights
        scaler.step(optimizer)
        # update the scale for next iteration
        scaler.update()

        #print("Iter Num: ", batch_idx)
        '''
        if (batch_idx == 10):
            #print("Iter Num: ", batch_idx)
            break
        '''
        #print("Iter Num: ", len(train_dataloader))
        #print("Iter Num: ", batch_idx)
        total_lossQCQ.append(loss.item())
    loss_listQCQ.append(sum(total_lossQCQ)/len(total_lossQCQ))
    print('Training [{:.0f}%]\tLoss: {:.4f}'.format(
        100. * (epoch + 1) / num_epochs, loss_listQCQ[-1]))
print('Training End Time (in sec) : ',(time.time()))
print('Total training time (in sec)', ((time.time() - startTimeQCQ)))
print('Training End Time',(time.asctime( time.localtime(time.time()) )))
print('end')
```

    --- Beginning Training! ---
    Training Start Time (in sec) :  1638404631.9463086
    Training Start Time :  Thu Dec  2 00:23:51 2021

    /usr/local/lib/python3.7/dist-packages/qiskit/utils/deprecation.py:62: DeprecationWarning: Using a qobj for run() is deprecated as of qiskit-aer 0.9.0 and will be removed no sooner than 3 months from that release date. Transpiled circuits should now be passed directly using `backend.run(circuits, **run_options).
      return func(*args, **kwargs)
    /usr/local/lib/python3.7/dist-packages/torch/autocast_mode.py:141: UserWarning: User provided device_type of 'cuda', but CUDA is not available. Disabling
      warnings.warn('User provided device_type of \'cuda\', but CUDA is not available. Disabling')
    /usr/local/lib/python3.7/dist-packages/torch/nn/modules/loss.py:520: UserWarning: Using a target size (torch.Size([4])) that is different to the input size (torch.Size([1, 4])). This will likely lead to incorrect results due to broadcasting. Please ensure they have the same size.
      return F.mse_loss(input, target, reduction=self.reduction)

    Training [5%]	Loss: 0.0853
    Training [10%]	Loss: 0.0896
    Training [15%]	Loss: 0.0905
    Training [20%]	Loss: 0.0855
    Training [25%]	Loss: 0.0885
    Training [30%]	Loss: 0.0818
    Training [35%]	Loss: 0.0800
    Training [40%]	Loss: 0.0806
    Training [45%]	Loss: 0.0991
    Training [50%]	Loss: 0.0817
    Training [55%]	Loss: 0.0815
    Training [60%]	Loss: 0.0854
    Training [65%]	Loss: 0.0839
    Training [70%]	Loss: 0.0774
    Training [75%]	Loss: 0.0862
    Training [80%]	Loss: 0.0804
    Training [85%]	Loss: 0.0781
    Training [90%]	Loss: 0.0768
    Training [95%]	Loss: 0.0786
    Training [100%]	Loss: 0.0744
    Training End Time (in sec) :  1638405121.6958873
    Total training time (in sec) 489.7496647834778
    Training End Time Thu Dec  2 00:32:01 2021
    end

```python
plt.plot(loss_listQCQ)
plt.title('Hybrid LQNN Q-C-Q Training')
plt.xlabel('Training Iterations')
plt.ylabel('Loss')
#plt.show()
```

```python
print("--- Beginning Test! ---")
loss_list = []
lnn_model.eval()
total_loss = []
correct = 0
for batch_idx, (data, target) in enumerate(test_dataloader):
    data, target = data.to(device), target.to(device)
    data = torch.squeeze(data)
    with torch.cuda.amp.autocast():
        target_pred = solve_euler_lagrange(lnn_model.forward, data.float())
        #print(target_pred)
        #print(target)
        correct += target_pred.eq(target.view_as(target_pred)).sum().item()
        loss = loss_fcn(target_pred.unsqueeze(0), target.float())
    total_loss.append(loss.item())
batch_size = 1
print('Performance on test data:[{:.0f}%]\tLoss: {:.4f}\tAccuracy: {:.1f}%'.format(
    100. * (epoch + 1) / num_epochs,
    sum(total_loss) / len(total_loss),
    correct / len(test_dataloader) * 100 / batch_size)
    )

print('end')
```

    --- Beginning Test! ---

    /usr/local/lib/python3.7/dist-packages/torch/autocast_mode.py:141: UserWarning: User provided device_type of 'cuda', but CUDA is not available. Disabling
      warnings.warn('User provided device_type of \'cuda\', but CUDA is not available. Disabling')
    /usr/local/lib/python3.7/dist-packages/qiskit/utils/deprecation.py:62: DeprecationWarning: Using a qobj for run() is deprecated as of qiskit-aer 0.9.0 and will be removed no sooner than 3 months from that release date. Transpiled circuits should now be passed directly using `backend.run(circuits, **run_options).
      return func(*args, **kwargs)

    Performance on test data:[100%]	Loss: 21.2050	Accuracy: 0.0%
    end

```python

```