Skip to content

PMM Tools Usage Guide

Welcome to pmm_tools! This interactive guide demonstrates how to build symbolic Hamiltonians for one-dimensional fermionic systems. We'll explore different ways to specify parameters and see exactly what symbolic expressions get generated.

Setup

Let's start by importing what we need and setting up better display for our symbolic expressions:

from sympy import symbols
import sympy
from IPython.display import display, Latex
import pmm_tools.models as models

# Make SymPy expressions display beautifully
from sympy import init_printing
init_printing()

Building Your First Hamiltonian: The Spinless Kitaev Chain

The interacting Kitaev chain is a perfect starting point—it includes hopping, superconducting pairing, Coulomb interactions, and chemical potential terms. Let's see how pmm_tools makes it easy to build these models symbolically.

Example 1: Start Simple with Defaults

When you don't specify parameters, each physical quantity becomes a single symbol that applies to the entire chain:

params, H, names = models.interacting_kiteav_chain(3)

print("Parameters generated:")
for key, value in params.items():
    print(f"   {key}: {value}")

print(f"\nFermionic operators: {names}")

print("\nThe resulting Hamiltonian:")
display(sympy.Eq(sympy.Symbol('H'), H))
findfont: Font family ['cmuserif'] not found. Falling back to DejaVu Sans.


findfont: Font family 'cmuserif' not found.


Parameters generated:
   mu: mu
   t: t
   Delta: Delta
   U: U

Fermionic operators: ['c_0', 'c_1', 'c_2']

The resulting Hamiltonian:

$\displaystyle H = \Delta {{c_0}^\dagger} {{c_1}^\dagger} + \Delta {{c_1}^\dagger} {{c_2}^\dagger} + \Delta {c_1} {c_0} + \Delta {c_2} {c_1} + U {{c_0}^\dagger} {c_0} {{c_1}^\dagger} {c_1} + U {{c_1}^\dagger} {c_1} {{c_2}^\dagger} {c_2} + \mu {{c_0}^\dagger} {c_0} + \mu {{c_1}^\dagger} {c_1} + \mu {{c_2}^\dagger} {c_2} + t {{c_0}^\dagger} {c_1} + t {{c_1}^\dagger} {c_0} + t {{c_1}^\dagger} {c_2} + t {{c_2}^\dagger} {c_1}$

Notice how each parameter (t, Delta, U, mu) is a single symbol that gets reused throughout the chain. This is perfect when you want uniform parameters.

Example 2: Mix Constants and Site-Dependent Parameters

Often you want some parameters to be the same everywhere (like hopping amplitude) but others to vary by site (like disorder in the chemical potential). Use declarative strings to control this:

params = {
    "t": "constant",          # Same hopping throughout
    "Delta": "constant",      # Uniform pairing
    "U": "space-dependent",   # Site-dependent interaction
    "mu": "space-dependent",  # Site-dependent chemical potential
}

params, H, names = models.interacting_kiteav_chain(3, params)

print("Parameter arrays:")
for key, value in params.items():
    print(f"   {key}: {value}")

print("\nHamiltonian with mixed parameter types:")
display(sympy.Eq(sympy.Symbol('H'), H))
findfont: Font family 'cmuserif' not found.


Parameter arrays:
   t: t
   Delta: Delta
   U: [U_0, U_1]
   mu: [mu_0, mu_1, mu_2]

Hamiltonian with mixed parameter types:

$\displaystyle H = \Delta {{c_0}^\dagger} {{c_1}^\dagger} + \Delta {{c_1}^\dagger} {{c_2}^\dagger} + \Delta {c_1} {c_0} + \Delta {c_2} {c_1} + U_{0} {{c_0}^\dagger} {c_0} {{c_1}^\dagger} {c_1} + U_{1} {{c_1}^\dagger} {c_1} {{c_2}^\dagger} {c_2} + \mu_{0} {{c_0}^\dagger} {c_0} + \mu_{1} {{c_1}^\dagger} {c_1} + \mu_{2} {{c_2}^\dagger} {c_2} + t {{c_0}^\dagger} {c_1} + t {{c_1}^\dagger} {c_0} + t {{c_1}^\dagger} {c_2} + t {{c_2}^\dagger} {c_1}$

See how U and mu now have indexed versions (U_0, U_1, U_2, etc.) while t and Delta remain single symbols? This gives you fine control over which terms can vary spatially.

Example 3: Custom Symbols and Numerical Values

You have complete flexibility—use custom SymPy symbols, set numerical values, or mix both with declarative strings:

params = {
    "t": symbols("t_eff", real=True),     # Custom symbol name
    "Delta": symbols("Delta_eff", real=True),  # Another custom symbol
    "U": 0.1,                             # Numerical value
    "mu": "space-dependent",              # Still using declarative string
}

params, H, names = models.interacting_kiteav_chain(3, params)

print("Mixed parameter specification:")
for key, value in params.items():
    print(f"   {key}: {value}")

print("\nThe Hamiltonian with your custom symbols:")
display(sympy.Eq(sympy.Symbol('H'), H))
Mixed parameter specification:
   t: t_eff
   Delta: Delta_eff
   U: 0.1
   mu: [mu_0, mu_1, mu_2]

The Hamiltonian with your custom symbols:


findfont: Font family 'cmuserif' not found.

$\displaystyle H = \Delta_{eff} {{c_0}^\dagger} {{c_1}^\dagger} + \Delta_{eff} {{c_1}^\dagger} {{c_2}^\dagger} + \Delta_{eff} {c_1} {c_0} + \Delta_{eff} {c_2} {c_1} + \mu_{0} {{c_0}^\dagger} {c_0} + \mu_{1} {{c_1}^\dagger} {c_1} + \mu_{2} {{c_2}^\dagger} {c_2} + t_{eff} {{c_0}^\dagger} {c_1} + t_{eff} {{c_1}^\dagger} {c_0} + t_{eff} {{c_1}^\dagger} {c_2} + t_{eff} {{c_2}^\dagger} {c_1} + 0.1 {{c_0}^\dagger} {c_0} {{c_1}^\dagger} {c_1} + 0.1 {{c_1}^\dagger} {c_1} {{c_2}^\dagger} {c_2}$

Perfect! Notice how: - t_eff and Delta_eff appear with your chosen names - U is just the number 0.1 everywhere - mu still gets expanded to site-dependent symbols (mu_0, mu_1, mu_2)

Example 4: Complete Manual Control

For maximum flexibility, specify exactly what you want at each site. This is great for modeling specific physical situations like quantum dots with tailored potentials:

t, Delta, U, mu = symbols("t Delta U mu", real=True)

# Create a chemical potential profile that varies across the chain
params = {
    "t": t,
    "Delta": Delta,
    "U": U,
    "mu": [mu - U/2, mu - U, mu - U/2]  # Custom potential landscape
}

params, H, names = models.interacting_kiteav_chain(3, params)

print("Fully customized parameters:")
for key, value in params.items():
    print(f"   {key}: {value}")

print("\nHamiltonian with your custom potential profile:")
display(sympy.Eq(sympy.Symbol('H'), H))
Fully customized parameters:
   t: t
   Delta: Delta
   U: U
   mu: [-U/2 + mu, -U + mu, -U/2 + mu]

Hamiltonian with your custom potential profile:


findfont: Font family 'cmuserif' not found.

$\displaystyle H = \Delta {{c_0}^\dagger} {{c_1}^\dagger} + \Delta {{c_1}^\dagger} {{c_2}^\dagger} + \Delta {c_1} {c_0} + \Delta {c_2} {c_1} + U {{c_0}^\dagger} {c_0} {{c_1}^\dagger} {c_1} + U {{c_1}^\dagger} {c_1} {{c_2}^\dagger} {c_2} + t {{c_0}^\dagger} {c_1} + t {{c_1}^\dagger} {c_0} + t {{c_1}^\dagger} {c_2} + t {{c_2}^\dagger} {c_1} + \left(- U + \mu\right) {{c_1}^\dagger} {c_1} + \left(- \frac{U}{2} + \mu\right) {{c_0}^\dagger} {c_0} + \left(- \frac{U}{2} + \mu\right) {{c_2}^\dagger} {c_2}$

This creates a "valley" in the chemical potential—higher at the edges, lower in the middle. Perfect for trapping particles or creating interesting physics!

Under the hood: All these examples use helper.prepare_parameters() to normalize your input into consistent arrays, then define_operators() creates the fermionic operators before assembling the Hamiltonian.

Stepping Up: Spinful Systems with the Dot-ABS Chain

Now let's explore spinful systems! The dot-ABS (Andreev Bound State) chain includes spin-orbit coupling, Zeeman splitting, and spin-dependent interactions. Each site now hosts two fermionic operators (one per spin direction).

params = {
    "t_n": "constant",        # Normal hopping (preserves spin)
    "t_so": "constant",       # Spin-orbit coupling (flips spin)
    "Delta": "constant",      # Superconducting pairing
    "U": "constant",          # Coulomb interaction
    "mu": "space-dependent",  # Chemical potential (can vary by site)
    "E_z": "constant",        # Zeeman energy (magnetic field)
}

params, H, names = models.dot_abs_chain(3, params)

print("Spinful system parameters:")
for key, value in params.items():
    print(f"   {key}: {value}")

print(f"\nFermionic operators (now with spin!): {names}")
print("   Notice: each site i has operators c_{i,up} and c_{i,down}")

print("\nThe full spinful Hamiltonian:")
display(sympy.Eq(sympy.Symbol('H'), H))
Spinful system parameters:
   t_n: t_n
   t_so: t_so
   Delta: Delta
   U: U
   mu: [mu_0, mu_1, mu_2]
   E_z: E_z

Fermionic operators (now with spin!): [c_{0\uparrow}, c_{0\downarrow}, c_{1\uparrow}, c_{1\downarrow}, c_{2\uparrow}, c_{2\downarrow}]
   Notice: each site i has operators c_{i,up} and c_{i,down}

The full spinful Hamiltonian:


findfont: Font family 'cmuserif' not found.

$\displaystyle H = \Delta {{c_{0\uparrow}}^\dagger} {{c_{0\downarrow}}^\dagger} + \Delta {{c_{1\uparrow}}^\dagger} {{c_{1\downarrow}}^\dagger} + \Delta {{c_{2\uparrow}}^\dagger} {{c_{2\downarrow}}^\dagger} + U {{c_{0\uparrow}}^\dagger} {c_{0\uparrow}} {{c_{0\downarrow}}^\dagger} {c_{0\downarrow}} + U {{c_{1\uparrow}}^\dagger} {c_{1\uparrow}} {{c_{1\downarrow}}^\dagger} {c_{1\downarrow}} + U {{c_{2\uparrow}}^\dagger} {c_{2\uparrow}} {{c_{2\downarrow}}^\dagger} {c_{2\downarrow}} + t_{n} \left({{c_{0\downarrow}}^\dagger} {c_{1\downarrow}} + {{c_{1\downarrow}}^\dagger} {c_{0\downarrow}}\right) + t_{n} \left({{c_{0\uparrow}}^\dagger} {c_{1\uparrow}} + {{c_{1\uparrow}}^\dagger} {c_{0\uparrow}}\right) + t_{n} \left({{c_{1\downarrow}}^\dagger} {c_{2\downarrow}} + {{c_{2\downarrow}}^\dagger} {c_{1\downarrow}}\right) + t_{n} \left({{c_{1\uparrow}}^\dagger} {c_{2\uparrow}} + {{c_{2\uparrow}}^\dagger} {c_{1\uparrow}}\right) + t_{so} \left({{c_{0\downarrow}}^\dagger} {c_{1\uparrow}} + {{c_{1\uparrow}}^\dagger} {c_{0\downarrow}}\right) - t_{so} \left({{c_{0\uparrow}}^\dagger} {c_{1\downarrow}} + {{c_{1\downarrow}}^\dagger} {c_{0\uparrow}}\right) + t_{so} \left({{c_{1\downarrow}}^\dagger} {c_{2\uparrow}} + {{c_{2\uparrow}}^\dagger} {c_{1\downarrow}}\right) - t_{so} \left({{c_{1\uparrow}}^\dagger} {c_{2\downarrow}} + {{c_{2\downarrow}}^\dagger} {c_{1\uparrow}}\right) + \left(- E_{z} + \mu_{0}\right) {{c_{0\downarrow}}^\dagger} {c_{0\downarrow}} + \left(- E_{z} + \mu_{1}\right) {{c_{1\downarrow}}^\dagger} {c_{1\downarrow}} + \left(- E_{z} + \mu_{2}\right) {{c_{2\downarrow}}^\dagger} {c_{2\downarrow}} + \left(E_{z} + \mu_{0}\right) {{c_{0\uparrow}}^\dagger} {c_{0\uparrow}} + \left(E_{z} + \mu_{1}\right) {{c_{1\uparrow}}^\dagger} {c_{1\uparrow}} + \left(E_{z} + \mu_{2}\right) {{c_{2\uparrow}}^\dagger} {c_{2\uparrow}} + \overline{\Delta} {c_{0\downarrow}} {c_{0\uparrow}} + \overline{\Delta} {c_{1\downarrow}} {c_{1\uparrow}} + \overline{\Delta} {c_{2\downarrow}} {c_{2\uparrow}}$

What's new here?

  • Two operators per site: c_{i,up} and c_{i,down} for spin-up and spin-down

  • Spin-orbit terms: t_so creates hopping that flips spin

  • Zeeman splitting: E_z shifts the energy of up/down spins differently