qlat.instanton_map — Instanton Detection and Topological Charge Mapping

Source: qlat/qlat/instanton_map.py

Note: Update this document when updating the source file.

Outline

  1. Overview

  2. Plaq-Dependent Flow

  3. Plaquette Coordinate Utilities

  4. Instanton List Processing

  5. Display Utilities

  6. InstantonMap Class

  7. Top-Level Computation

  8. Examples


Overview

instanton_map provides tools for detecting and tracking instantons (and anti-instantons) in lattice gauge field configurations. The module uses modified Wilson flow with plaquette-dependent couplings to either freeze, shrink, or localize topological structures, then identifies instanton candidates by grouping lattice sites with anomalously low plaquette values.

The key idea is that instantons manifest as localized regions where the plaquette deviates significantly from unity. By flowing the gauge field with specialized flow types, one can either preserve the topological charge (freeze flow) or enhance tunnelling (shrink flow), enabling robust instanton counting and topological charge estimation.

Standard Wilson flow action: $\( S_\mathrm{Wilson} = \frac{\beta}{2}\sum_{x,\mu,\nu} \Big(1 - \frac{1}{3}\mathrm{Re}\mathrm{Tr} U_{\mu,\nu}\Big) \)$

Modified flow with plaquette-dependent coupling: $\( S_f = -\frac{\beta}{2}\sum_{x,\mu,\nu} f\Big(\frac{1}{3}\mathrm{Re}\mathrm{Tr} U_{\mu,\nu}\Big) \)$


Plaq-Dependent Flow

gf_flow_topo

gf_flow_topo(
    gf: GaugeField,
    step_size: float,
    flow_type: str | float | None = None,
    wilson_flow_integrator_type: str | None = None,
) -> float

Perform one step of plaquette-dependent flow on gf in place. Returns step_size / norm, the effective step size used.

For standard Symanzik flows ("Wilson", "Iwasaki", "DBW2", or a numeric c1 value), delegates to gf_wilson_flow_step with Euler integrator by default.

For specialized flow types, a plaquette-dependent force is computed:

Flow Type

Description

Parameters

"Wilson"

Standard Wilson flow (c1 = 0.0)

"Iwasaki"

Iwasaki action (c1 = -0.331)

"DBW2"

DBW2 action (c1 = -1.4008)

"Freeze"

Suppresses plaquette deviations; prevents topological tunnelling

"Shrink"

Shrinks instanton size; enhances topological tunnelling

eps=0.005, b=0.5

"Localize"

Shrinks large instantons, prevents small instanton tunnelling

eps=0.002, b=50

"Preserve"

Mimics Wilson flow, prevents small instanton tunnelling

b=50

The freeze derivative: df/dp = 1 - p The shrink derivative: df/dp = eps / (1 - p + eps) + b The localize derivative: df/dp = eps / (1 - p + eps) + b * (1 - p) The preserve derivative: df/dp = max(1, b * (1 - p))

gf_energy_derivative_density_field_topo

gf_energy_derivative_density_field_topo(
    gf: GaugeField,
    *,
    epsilon: float = 0.0125,
    flow_typw: str | float | None = None,
    wilson_flow_integrator_type: str | None = None,
) -> FieldRealD

Compute the derivative dE/dt of the energy density with respect to flow time using a symmetric finite difference of step 2 * epsilon. Supports the same flow types as gf_flow_topo. Returns a FieldRealD with multiplicity 1.


Plaquette Coordinate Utilities

mk_plaq_xg_arr

mk_plaq_xg_arr(geo: Geometry) -> np.ndarray

Build an array of plaquette center coordinates for all local sites. Returns an array of shape (n_points, 6, 4) where n_points is the local volume and the 6 plaquette directions are: xy, xz, xt, yz, yt, zzt. Each plaquette center is offset by 0.5 in the two directions defining the plaquette.

get_extreme_plaq_xg_list

get_extreme_plaq_xg_list(
    plaq_xg_arr: np.ndarray,
    f_plaq: FieldRealD,
    threshold: float,
) -> list[tuple[float, list[float]]]

Return a globally sorted list of (plaq, xg) tuples for all plaquettes with plaq < 1 - threshold. These are plaquette sites potentially associated with instanton structures.

get_group_extreme_plaq_xg_list

get_group_extreme_plaq_xg_list(
    extreme_plaq_xg_list: list,
    total_site: Coordinate,
    dis_sqr_limit: float,
) -> list[list[tuple[float, list[float], float]]]

Group extreme plaquette points by proximity. Points within squared distance dis_sqr_limit (using smearing-aware periodic distance) are placed in the same group. Each group is represented as a list of (plaq, xg, dis_sq) tuples, where dis_sq is the squared distance from the group’s first point.


Instanton List Processing

process_inst_list

process_inst_list(inst_list: list) -> list[dict]

Process the raw instanton tracking data from InstantonMap.inst_list into a structured list of per-instanton summary dictionaries.

Each entry in the returned p_inst_list contains:

Key

Description

inst_idx

Original instanton index

inst_raw

Raw tracking data

o_xg_d

Coordinate on initial lattice (float)

o_total_site

Total site size on initial lattice

xg_d

Coordinate at detection (float)

total_site

Total site size at detection

current_spacing

Lattice spacing ratio

plaq

Minimum plaquette value

flow_time

Flow time at minimum plaq

num_plaq

Number of plaquettes in the group

dis_sqr_max

Max squared distance within group

flow_time_start / flow_time_end

Flow time range of tracking

delta_s_topo

Change in topological charge over flow

estimate_topo_charge

Estimated topological charge

closest_inst_info

Info about the closest instanton

closest_sim_inst_info

Info about the closest instanton with overlapping flow time

get_tot_topo_count

get_tot_topo_count(p_inst_list: list[dict], flow_time: float) -> int

Get the total topological charge by summing the rounded estimated topological charges of instantons detected after flow_time.

get_tot_inst_count

get_tot_inst_count(p_inst_list: list[dict], flow_time: float) -> int

Get the total number of instantons plus anti-instantons detected after flow_time.


Display Utilities

displayln_info_topo_info

displayln_info_topo_info(topo_info: dict) -> None

Print a formatted summary of a topological info record (flow time, plaquette, topological charge, number of instantons, etc.).

displayln_info_p_inst

displayln_info_p_inst(p_inst: dict) -> None

Print a detailed summary of a single processed instanton record.

displayln_info_p_inst_list

displayln_info_p_inst_list(p_inst_list: list[dict]) -> None

Print summaries for all processed instantons and the cumulative topological charge.


InstantonMap Class

InstantonMap.__init__

InstantonMap(
    *,
    total_site: Coordinate = None,
    topo_sphere_sum_radius_list: list[float] = None,
    dis_sqr_limit: float = None,
    threshold: float = None,
)

Initialize an instanton tracker.

Parameter

Description

total_site

Lattice dimensions (e.g., q.Coordinate([4, 4, 4, 8]))

topo_sphere_sum_radius_list

Radii for sphere-sum topological charge

dis_sqr_limit

Squared distance limit to identify same instanton

threshold

Plaquette threshold: sites with plaq < 1 - threshold are instanton candidates

InstantonMap.shift

InstantonMap.shift(shift: Coordinate) -> None

Record a shift of the gauge field. Updates origin_coordinate so that instanton coordinates remain in the initial lattice frame.

InstantonMap.convert_xg

InstantonMap.convert_xg(xg: Coordinate | CoordinateD) -> Coordinate | CoordinateD

Convert a coordinate in the current lattice frame to the initial lattice frame.

InstantonMap.half_lattice

InstantonMap.half_lattice() -> None

Double the lattice spacing (halve the lattice size). Used when the gauge field is coarsened during multi-scale instanton detection.

InstantonMap.acc_time

InstantonMap.acc_time(step_size: float) -> None

Accumulate flow time and increment the step counter after a flow step.

InstantonMap.acc_topo_info

InstantonMap.acc_topo_info(
    f_plaq: FieldRealD,
    f_topo: FieldRealD | None = None,
) -> None

Detect and track instantons from the current plaquette field. Groups extreme plaquette sites, matches them to previously tracked instantons, and updates the active instanton list. If f_topo is provided, computes sphere-sum topological charge at each instanton location.


Top-Level Computation

compute_inst_map

compute_inst_map(
    gf: GaugeField,
    *,
    dis_sqr_limit: float = None,
    threshold: float = None,
    topo_sphere_sum_radius_list: list[float] = None,
    plaq_min_threshold: float = None,
    max_spacing: int = None,
    flow_step_size_list: list[float] = None,
    flow_num_step_list: list[int] = None,
) -> dict

Run the full instanton map computation on a gauge field. Returns a dictionary with keys inst_list, info_list, flow_time_list, tot_topo_list.

The algorithm proceeds in three phases:

  1. Wilson flow — smooth the field with standard Wilson flow

  2. Freeze flow — freeze topological structure by suppressing plaquette deviations

  3. Shrink flow — shrink instantons to detect and count them, optionally halving the lattice when the field is smooth enough

Parameter

Default

Description

dis_sqr_limit

5

Squared distance for instanton grouping

threshold

0.1

Plaquette deviation threshold

topo_sphere_sum_radius_list

[2, 3, 4, 5, 6]

Radii for sphere-sum topological charge

plaq_min_threshold

0.97^(1/16)

Min plaq threshold for lattice halving

max_spacing

auto

Maximum lattice spacing (power of 2)

flow_step_size_list

[0.05, 0.1, 0.1]

Step sizes for Wilson, Freeze, Shrink

flow_num_step_list

[80, 320, 1000000]

Max steps for Wilson, Freeze, Shrink

displayln_info_inst_map_obj

displayln_info_inst_map_obj(inst_map_obj: dict) -> None

Display a comprehensive summary of the instanton map results, including per-instanton details and topological charge verification.

smear_measure_topo

smear_measure_topo(
    gf: GaugeField,
    smear_info_list: list[list] = None,
    *,
    energy_derivative_info: list = None,
    info_path: str = None,
    density_field_path: str = None,
    is_show_topo_terms: bool = False,
) -> tuple[list[dict], list[dict]]

Apply a sequence of smearing steps to gf in place, measuring topological observables after each step. Returns (topo_list, energy_list).

Each entry in smear_info_list is [step_size, n_step, flow_type, wilson_flow_integrator_type].

The topo_list entries contain: flow_time, plaq, plaq_min, plaq_max, energy_density, energy_deriv, topo, topo_clf, abs_topo, and their per-timeslice variants.

The energy_list entries contain: flow_time, plaq, plaq_min, plaq_max, energy_density, and per-timeslice values.


Examples

Compute Instanton Map

import qlat as q

size_node_list = [
    [1, 1, 1, 1],
    [1, 1, 1, 2],
    [1, 1, 1, 4],
    [1, 1, 1, 8],
]

q.begin_with_mpi(size_node_list)

total_site = q.Coordinate([4, 4, 4, 8])
geo = q.Geometry(total_site)
gf = q.GaugeField(geo)
rng = q.RngState("seed")
gf.set_rand(rng)

obj = q.compute_inst_map(gf)

inst_list = obj["inst_list"]
info_list = obj["info_list"]
flow_time_list = obj["flow_time_list"]
tot_topo_list = obj["tot_topo_list"]

print(f"Number of detected instantons: {len(inst_list)}")
print(f"Topological charge history: {tot_topo_list}")

q.end_with_mpi()

Process and Display Instanton List

import qlat as q

size_node_list = [
    [1, 1, 1, 1],
    [1, 1, 1, 2],
    [1, 1, 1, 4],
    [1, 1, 1, 8],
]

q.begin_with_mpi(size_node_list)

total_site = q.Coordinate([4, 4, 4, 8])
geo = q.Geometry(total_site)
gf = q.GaugeField(geo)
rng = q.RngState("seed")
gf.set_rand(rng)

obj = q.compute_inst_map(gf)
q.displayln_info_inst_map_obj(obj)

q.end_with_mpi()

Smear and Measure Topology

import qlat as q

size_node_list = [
    [1, 1, 1, 1],
    [1, 1, 1, 2],
    [1, 1, 1, 4],
    [1, 1, 1, 8],
]

q.begin_with_mpi(size_node_list)

total_site = q.Coordinate([4, 4, 4, 8])
geo = q.Geometry(total_site)
gf = q.GaugeField(geo)
rng = q.RngState("seed")
gf.set_rand(rng)

smear_info_list = [
    [0.05, 20, "Wilson", "euler"],
    [0.05, 20, "Wilson", "euler"],
    [0.01, 50, "DBW2", "euler"],
]

topo_list, energy_list = q.smear_measure_topo(gf, smear_info_list)

for entry in topo_list:
    print(f"t={entry['flow_time']:.4f}  topo={entry['topo']:.4f}  plaq={entry['plaq']:.6f}")

q.end_with_mpi()

Plaq-Dependent Flow with Different Flow Types

import qlat as q

size_node_list = [
    [1, 1, 1, 1],
    [1, 1, 1, 2],
    [1, 1, 1, 4],
    [1, 1, 1, 8],
]

q.begin_with_mpi(size_node_list)

total_site = q.Coordinate([4, 4, 4, 8])
geo = q.Geometry(total_site)
gf = q.GaugeField(geo)
rng = q.RngState("seed")
gf.set_rand(rng)

step_size = 0.05

# Wilson flow
gf_w = gf.copy()
q.gf_flow_topo(gf_w, step_size, "Wilson")
print(f"After Wilson flow: plaq={gf_w.plaq():.6f}")

# Freeze flow
gf_f = gf.copy()
q.gf_flow_topo(gf_f, step_size, "Freeze")
print(f"After Freeze flow: plaq={gf_f.plaq():.6f}")

# Shrink flow
gf_s = gf.copy()
q.gf_flow_topo(gf_s, step_size, "Shrink")
print(f"After Shrink flow: plaq={gf_s.plaq():.6f}")

q.end_with_mpi()