<jupyter_start><jupyter_text>Working with composite Hilbert spaces and interfacing with QuTiPSystems of interest for quantum information processing will involve multiple qubits as well as oscillators with mutual coupling. The resulting Hilbert space is the tensor product of the individual constituent Hilbert spaces. The `HilbertSpace` class allows one to define such coupled systems, to define the interactions between them, and to contruct the overall Hamiltonian. From this, dressed eigenenergies and eigenstates can be extracted. The operator matrices and state vectors  at the `HilbertSpace` level are given as QuTiP `Qobj` instances. This interface to QuTiP is particularly helpful if the task at hand is the simulation of time-dependent dynamics of the coupled system, perhaps including additional drive terms.  Example: two transmon qubits coupled capacitively to one resonator modeA simple but useful example of a coupled quantum system is the case of two transmons, each coupled capacitively to a standing-wave mode inside a microwave resonator. The Hamiltonian describing this composite system is given by$\displaystyle H=\sum_{\alpha=1,2}\sum_{j} E_j^{(\alpha)}| j^{(\alpha)}\rangle\langle  j^{(\alpha)}| + \omega_r a^\dagger a + \sum_{\alpha=1,2}g^{(\alpha)} n_\alpha(a+a^\dagger)$,where $\alpha=1,2$ labels the two transmons; $E_j^\alpha$ and $|j^{(\alpha)} \rangle$ are  the $j$-th eigenenergy and corresponding eigenstate of qubit $\alpha$. Resonator photons with (angular) frequency $\omega$ are created by the usual ladder operator $a^\dagger$. The resonator-transmon coupling is facilitated by the interaction between the qubit's dipolar charge $n_\alpha$ and the resonator's electric field $\sim a+a^\dagger$, and $g^{(\alpha)}$ parametrizes the coupling strength. Define Hilbert space components, initialize `HilbertSpace` objectTo set up the Hilbert space of two transmons and one resonator, we first define the separate components and then initialize a `HilbertSpace` object by submitting the list of all three subsystems:<jupyter_code># Set up the components / subspaces of our Hilbert space

CPB1 = scq.Transmon(
    EJ = 40.0,
    EC = 0.2,
    ng = 0.3,
    ncut = 40,
    truncated_dim = 3     # after diagonalization, we will keep 3 levels
)

CPB2 = scq.Transmon(
    EJ = 30.0,
    EC = 0.15,
    ng = 0.0,
    ncut = 10,
    truncated_dim = 4
)

resonator = scq.Oscillator(
    E_osc = 6.0,
    truncated_dim=4  # up to 3 photons (0,1,2,3)
)


# Form a list of all components making up the Hilbert space.
hilbertspace = HilbertSpace([CPB1, CPB2, resonator])
print(hilbertspace)<jupyter_output>====== HilbertSpace object ======

TRANSMON
 ———— PARAMETERS ————
EJ	: 40.0
EC	: 0.2
ng	: 0.3
ncut	: 40
truncated_dim	: 3
Hilbert space dimension	: 81

TRANSMON
 ———— PARAMETERS ————
EJ	: 30.0
EC	: 0.15
ng	: 0.0
ncut	: 10
truncated_dim	: 4
Hilbert space dimension	: 21

OSCILLATOR
 ———— PARAMETERS ————
truncated_dim	: 4
E_osc	: 6.0
Hilbert space dimension	: 4<jupyter_text>While we yet have to set up the interactions between the components, we can already obtain the bare Hamiltonian of the non-interacting subsystems, expressed as a matrix in the joint Hilbert space:<jupyter_code>bare_hamiltonian = hilbertspace.bare_hamiltonian()
bare_hamiltonian<jupyter_output><empty_output><jupyter_text>The Hamiltonian matrix is given in the form of a QuTiP quantum object which facilitates simple incorporation of Hamiltonians generated with `scqubits` into any of the solvers QuTiP offers for time evolution.  Set up interaction terms between individual subsystems<jupyter_code>g1 = 0.1  # coupling resonator-CPB1 (without charge matrix elements)
g2 = 0.2  # coupling resonator-CPB2 (without charge matrix elements)

interaction1 = InteractionTerm(
    g_strength = g1,
    op1 = CPB1.n_operator(),
    subsys1 = CPB1,
    op2 = resonator.creation_operator() + resonator.annihilation_operator(),
    subsys2 =resonator
)

interaction2 = InteractionTerm(
    g_strength = g2,
    op1 = CPB2.n_operator(),
    subsys1 = CPB2,
    op2 = resonator.creation_operator() + resonator.annihilation_operator(),
    subsys2 =resonator
)<jupyter_output><empty_output><jupyter_text>Now insert the interaction terms into the HilbertSpace object.<jupyter_code>interaction_list = [interaction1, interaction2]

hilbertspace.interaction_list = interaction_list<jupyter_output><empty_output><jupyter_text>Now that the interactions are specified, the full Hamiltonian of the coupled system can be obtained via:<jupyter_code>dressed_hamiltonian = hilbertspace.hamiltonian()
dressed_hamiltonian<jupyter_output><empty_output><jupyter_text>Since the composite Hamiltonian is a `qutip.Qobj`, The eigenvalues and eigenvectors can be now be obtained via the usual QuTiP routine:<jupyter_code>evals, evecs = dressed_hamiltonian.eigenstates(eigvals=4)
print(evals)<jupyter_output>[-63.09882162 -57.51117008 -56.87227527 -55.29218444]<jupyter_text>GUI use for `HilbertSpace` object creationAs an alternative to the programmatic generation of a new `HilbertSpace` object, the following GUI-based creation process is supported:<jupyter_code>hilbertspace_new = scq.HilbertSpace.create()<jupyter_output><empty_output><jupyter_text>Spectrum lookup and converting between bare and dressed indicesTo use lookup functions for state indices, energies and states, first generate the lookup table via:<jupyter_code>hilbertspace.generate_lookup()<jupyter_output><empty_output><jupyter_text>Here are the bare energies of the first Cooper pair box system:<jupyter_code>hilbertspace.lookup.bare_eigenenergies(CPB1)<jupyter_output><empty_output><jupyter_text>The dressed state with index j=3 corresponds to following bare product state:<jupyter_code>hilbertspace.lookup.bare_index(8)<jupyter_output><empty_output><jupyter_text>And the bare product state (1,2,1) most closely matches the following dressed state:<jupyter_code>hilbertspace.lookup.dressed_index((1,2,1))
hilbertspace.lookup.energy_dressed_index(10)
hilbertspace.lookup.bare_eigenstates(CPB1);<jupyter_output><empty_output><jupyter_text>Sweeping over an external parameterscqubits provides the class `ParameterSweep` to facilitate computation of spectra as function of an external parameter. For efficient usage in the `Explorer` class, computed bare and dressed spectral data are stored internally. A sweep object is initialized as follows.Consider the case where both `CPB1` and `CPB2` subsystems are flux-tunable split-junction transmons with different loop areas.<jupyter_code>param_name = 'flux'                          # name of varying external parameter
param_vals = np.linspace(0., 2.0, 200)     # parameter values

subsys_update_list = [CPB1, CPB2]                  # list of HilbertSpace subsystems which are affected by parameter changes         

def update_hilbertspace(param_val):     # function that shows how Hilbert space components are updated
    CPB1.EJ = 20*np.abs(np.cos(np.pi * param_val))
    CPB2.EJ = 15*np.abs(np.cos(np.pi * param_val * 0.65))

    
sweep = ParameterSweep(
    param_name=param_name,
    param_vals=param_vals,
    evals_count=20,
    hilbertspace=hilbertspace,
    subsys_update_list=subsys_update_list,
    update_hilbertspace=update_hilbertspace
)<jupyter_output><empty_output><jupyter_text>Once the data has been generated, the spectrum can be plotted as a function of the parameter value:<jupyter_code>splot.dressed_spectrum(sweep);
sweep.filewrite('./sweep-test.h5')
storedsweep = scq.read('./sweep-test.h5')
splot.dressed_spectrum(storedsweep)
hspace_stored = storedsweep.get_hilbertspace()
[tmon1, tmon2, osc] = hspace_stored
storedsweep.lookup.bare_eigenenergies(tmon1)
storedsweep.lookup.bare_index(9)
storedsweep.lookup.energy_dressed_index(10,100)<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.08312958435207823, 'fraction_numerical': 0.023163042079526446, 'mean_word_length': 5.460515378221114, 'pattern_counts': {'":': 0, '<': 51, '<?xml version=': 0, '>': 51, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '13', 'dir': 'jupyter-structured-clean-dedup', 'id': '390389', 'license': 'BSD-3-Clause', 'n_tokens_mistral': 2582, 'n_tokens_neox': 2385, 'n_words': 774, 'path': 'examples/demo_hilbertspace.ipynb', 'repo_name': 'ooovector/scqubits', 'size': '7771'}