Skip to content

harmonic_oscillator

Harmonic oscillator model.

HarmonicOscillatorOrbitals (Module) dataclass

Create orbitals for a set of non-interacting quantum harmonic oscillators.

The single-particle quantum harmonic oscillator for particle i has the Hamiltonian

H = -(1/2) d^2/dx^2 + (1/2) (omega * x)^2,

where the first term is the second derivative/1-D Laplacian and the second term is the harmonic oscillator potential. The corresponding energy eigenfunctions take the simple form

psi_n(x) = C * exp(-omega * x^2 / 2) * H_n(sqrt(omega) * x),

where C is a normalizing constant and H_n is the nth Hermite polynomial. The corresponding energy level is simply E_n = omega * (n + (1/2)).

With N non-interacting particles, the total many-body Hamiltonian is simply H = sum_i H_i, and the above single-particle energy eigenfunctions become analogous to molecular orbitals. Due to the antisymmetry requirement on particle states, the N particles cannot occupy the same orbitals, so in the ground state configuration, the particles fill the lowest energy orbitals first. If some fixed spin configuration is specified, then the corresponding ground state configuration is the one where the particles fill the lowest energy orbitals per spin.

For a single spin, the model evaluates the lowest n energy orbitals, where n is the number of particles for that spin. In this model, each spin corresponds to a leaf in the input pytree x.

If x is a single array (spinless input), then when this omega matches the omega in the potential, then model.apply(params, x) evaluates the first n eigenfunctions of the Hamiltonian on x. If there are multiple leaves in x, each of which is a different spin, then this behavior is mapped over each leaf.

In other words, the .apply method has the signature

(params, [x]) -> [orbital_matrix],

where x has shape (..., n, 1) and orbital_matrix has shape (..., n, n), and the bracket notation is used to denote a pytree structure of spin. For example, if the inputs are (params, {"up": x_up, "down": x_down}), then the output will be {"up": orbitals_up, "down": orbitals_down}. The params consist of a single number, a model-specific omega.

Because the particles are completely non-interacting in this model, there are no interactions computed between the leaves of the input pytree (or even between particles in a single leaf).

Attributes:

Name Type Description
omega_init jnp.float32

initial value for omega in the model; when this matches the omega in the potential energy, then these orbitals become eigenfunctions

__call__(self, xs) special

Compute the harmonic oscillator orbitals on each leaf of xs.

Source code in vmcnet/examples/harmonic_oscillator.py
def __call__(self, xs):
    """Compute the harmonic oscillator orbitals on each leaf of xs."""
    return jax.tree_map(self._single_leaf_call, xs)

make_hermite_polynomials(x)

Compute the first n Hermite polynomials evaluated at n points.

Because the Hermite polynomials are orthogonal, they have a three-term recurrence given by

H_n(x) = 2 * x * H_{n-1}(x) - 2 * (n - 1) * H_{n-2}(x).

When there are n particles x_1, ..., x_n, this function simply evaluates the first n Hermite polynomials at these positions to form an nxn matrix with the particle index along the first axis and the polynomials along the second.

Parameters:

Name Type Description Default
x Array

an array of shape (..., nparticles, 1)

required

Returns:

Type Description
Array

an array of shape (..., nparticles, nparticles), where the (..., i, j)th entry corresponds to H_j(x_i).

Source code in vmcnet/examples/harmonic_oscillator.py
def make_hermite_polynomials(x: Array) -> Array:
    """Compute the first n Hermite polynomials evaluated at n points.

    Because the Hermite polynomials are orthogonal, they have a three-term recurrence
    given by

        H_n(x) = 2 * x * H_{n-1}(x) - 2 * (n - 1) * H_{n-2}(x).

    When there are n particles x_1, ..., x_n, this function simply evaluates the first n
    Hermite polynomials at these positions to form an nxn matrix with the particle index
    along the first axis and the polynomials along the second.

    Args:
        x (Array): an array of shape (..., nparticles, 1)

    Returns:
        Array: an array of shape (..., nparticles, nparticles), where the
        (..., i, j)th entry corresponds to H_j(x_i).
    """
    nparticles = x.shape[-2]
    if nparticles == 1:
        return jnp.ones_like(x)

    H = [jnp.ones_like(x), 2 * x]
    for n in range(2, nparticles):
        H.append(2 * x * H[n - 1] - 2 * (n - 1) * H[n - 2])

    return jnp.concatenate(H, axis=-1)

make_harmonic_oscillator_spin_half_model(nspin_first, model_omega_init)

Create a spin-1/2 quantum harmonic oscillator wavefunction (two spins).

Parameters:

Name Type Description Default
nspin_first int

number of the alpha spin type, where the number of spins of each type is (alpha, beta); the model assumes that its input along the second-to-last dimension has size alpha + beta

required
model_omega_init jnp.float32

spring constant inside the model; when it matches the spring constant in the hamiltonian, then this model evaluates an eigenstate

required

Returns:

Type Description
models.core.Module

spin-1/2 wavefunction for the quantum harmonic oscillator, with one trainable parameter (the model omega)

Source code in vmcnet/examples/harmonic_oscillator.py
def make_harmonic_oscillator_spin_half_model(
    nspin_first: int, model_omega_init: jnp.float32
) -> models.core.Module:
    """Create a spin-1/2 quantum harmonic oscillator wavefunction (two spins).

    Args:
        nspin_first (int): number of the alpha spin type, where the number of spins of
            each type is (alpha, beta); the model assumes that its input along the
            second-to-last dimension has size alpha + beta
        model_omega_init (jnp.float32): spring constant inside the model; when it
            matches the spring constant in the hamiltonian, then this model evaluates an
            eigenstate

    Returns:
        models.core.Module: spin-1/2 wavefunction for the quantum harmonic
        oscillator, with one trainable parameter (the model omega)
    """

    def split_spin_fn(x):
        return jnp.split(x, [nspin_first], axis=-2)

    orbitals = HarmonicOscillatorOrbitals(model_omega_init)
    logdet_fn = models.antisymmetry.logdet_product

    return models.core.ComposedModel([split_spin_fn, orbitals, logdet_fn])

harmonic_oscillator_potential(omega, x)

Potential energy for independent harmonic oscillators with spring constant omega.

This function computes sum_i 0.5 * (omega * x_i)^2. If x has more than one axis, these are simply summed over, so this corresponds to an isotropic harmonic oscillator potential.

This function should be vmapped in order to be applied to batches of inputs, as it expects the first axis of x to correspond to the particle index.

Parameters:

Name Type Description Default
omega jnp.float32

spring constant

required
x Array

array of particle positions, where the first axis corresponds to particle index

required

Returns:

Type Description
jnp.float32

potential energy value for this configuration x

Source code in vmcnet/examples/harmonic_oscillator.py
def harmonic_oscillator_potential(omega: jnp.float32, x: Array) -> jnp.float32:
    """Potential energy for independent harmonic oscillators with spring constant omega.

    This function computes sum_i 0.5 * (omega * x_i)^2. If x has more than one axis,
    these are simply summed over, so this corresponds to an isotropic harmonic
    oscillator potential.

    This function should be vmapped in order to be applied to batches of inputs, as it
    expects the first axis of x to correspond to the particle index.

    Args:
        omega (jnp.float32): spring constant
        x (Array): array of particle positions, where the first axis corresponds
            to particle index

    Returns:
        jnp.float32: potential energy value for this configuration x
    """
    return 0.5 * jnp.sum(jnp.square(omega * x))

make_harmonic_oscillator_local_energy(omega, log_psi_apply)

Factory to create a local energy fn for the harmonic oscillator log|psi|.

Parameters:

Name Type Description Default
omega jnp.float32

spring constant for the harmonic oscillator

required
log_psi_apply Callable

function which evaluates log|psi| for a harmonic oscillator model wavefunction psi. Has the signature (params, x) -> log|psi(x)|.

required

Returns:

Type Description
Callable

local energy function with the signature (params, x) -> local energy associated to the wavefunction psi

Source code in vmcnet/examples/harmonic_oscillator.py
def make_harmonic_oscillator_local_energy(
    omega: jnp.float32, log_psi_apply: ModelApply[P]
) -> ModelApply[P]:
    """Factory to create a local energy fn for the harmonic oscillator log|psi|.

    Args:
        omega (jnp.float32): spring constant for the harmonic oscillator
        log_psi_apply (Callable): function which evaluates log|psi| for a harmonic
            oscillator model wavefunction psi. Has the signature
            (params, x) -> log|psi(x)|.

    Returns:
        Callable: local energy function with the signature (params, x) -> local energy
        associated to the wavefunction psi
    """
    kinetic_fn = physics.kinetic.create_continuous_kinetic_energy(log_psi_apply)

    def potential_fn(params: P, x: Array):
        del params
        return harmonic_oscillator_potential(omega, x)

    potential_fn = jax.vmap(potential_fn, in_axes=(None, 0), out_axes=0)

    return physics.core.combine_local_energy_terms([kinetic_fn, potential_fn])