StructureOptimizer#

class pasted._optimizer.StructureOptimizer(*, n_atoms: int, charge: int, mult: int, objective: dict[str, float] | Callable[[dict[str, float]], float], elements: str | list[str] | None = None, method: str = 'annealing', max_steps: int = 5000, T_start: float = 1.0, T_end: float = 0.01, frag_threshold: float = 0.3, move_step: float = 0.5, allow_composition_moves: bool = True, allow_displacements: bool = True, lcc_threshold: float = 0.0, cov_scale: float = 1.0, relax_cycles: int = 1500, cutoff: float | None = None, n_bins: int = 20, w_atom: float = 0.5, w_spatial: float = 0.5, n_restarts: int = 1, n_replicas: int = 4, pt_swap_interval: int = 10, seed: int | None = None, verbose: bool = False)[source]#

Bases: object

Optimise a single structure to maximise a disorder objective.

Parameters:
  • n_atoms – Number of atoms.

  • charge – Total system charge.

  • mult – Spin multiplicity 2S+1.

  • objective – Weight dict {"METRIC": weight, ...} or any callable (metrics: dict[str, float]) -> float. The optimizer maximises the scalar value. Use negative weights to penalise a metric.

  • elements – Element pool — spec string ("6,7,8"), list of symbols, or None for all Z = 1–106.

  • method"annealing" (default), "basin_hopping", or "parallel_tempering".

  • max_steps – Number of MC steps per restart (or per replica per restart for "parallel_tempering"; default: 5000).

  • T_start – Initial temperature (default: 1.0). For "parallel_tempering" this is the highest replica temperature.

  • T_end – Final temperature for SA (default: 0.01). For "parallel_tempering" this is the lowest replica temperature (the coldest, most selective replica). BH uses T_start throughout.

  • n_replicas – Number of temperature replicas for "parallel_tempering" (default: 4). Ignored for other methods. Temperatures are spaced geometrically between T_end and T_start.

  • pt_swap_interval – Attempt a replica-exchange swap every this many MC steps (default: 10). Ignored for other methods.

  • allow_displacements – When True (default), atomic-position moves (fragment moves) are included in the MC step pool. When False, only composition moves are performed — atomic coordinates are held fixed and only element types are optimised. Set to False when exploring compositional disorder at fixed geometry (e.g. a pre-relaxed lattice). Cannot be False simultaneously with allow_composition_moves.

  • allow_composition_moves – When True (default), each MC step randomly chooses between a fragment move (atomic displacement) and a composition move (element-type swap) with equal probability. When False, only fragment moves are performed — element types are held fixed and only atomic positions are optimised. Set to False when the composition is predetermined and should not change during optimisation. Cannot be False simultaneously with allow_displacements.

  • frag_threshold – Local Q6 threshold for fragment selection (default: 0.3). Atoms with local Q6 > threshold are preferentially displaced.

  • move_step – Maximum displacement magnitude per coordinate step (Å, default: 0.5).

  • lcc_threshold – Minimum graph_lcc required to accept a step (default: 0.0, i.e. no connectivity constraint). Set to 0.8 to enforce that at least 80 % of atoms remain connected.

  • cov_scale – Minimum distance scale factor for relax_positions().

  • relax_cycles – Max repulsion-relaxation cycles per step. Basin-Hopping uses 3× this value for its local-minimisation step.

  • cutoff – Distance cutoff (Å) for Steinhardt / graph metrics. Auto-computed from the element pool when None.

  • n_bins – Histogram bins for H_spatial / RDF_dev (default: 20).

  • w_atom – Weight of H_atom in H_total (default: 0.5).

  • w_spatial – Weight of H_spatial in H_total (default: 0.5).

  • n_restarts – Independent optimisation runs (default: 1). The best result across all restarts is returned.

  • seed – Random seed (None → non-deterministic).

  • verbose – Print per-step progress to stderr (default: False).

Examples

Class API:

from pasted import StructureOptimizer

opt = StructureOptimizer(
    n_atoms=50,
    charge=0, mult=1,
    elements="24,25,26,27,28",      # Cantor alloy
    objective={"H_atom": 1.0, "H_spatial": 1.0, "Q6": -2.0},
    method="annealing",
    max_steps=5000,
    lcc_threshold=0.8,
    seed=42,
)
best = opt.run()

Callable objective:

opt = StructureOptimizer(
    ...,
    objective=lambda m: m["H_spatial"] - 2.0 * m["Q6"],
)
__repr__() str[source]#

Return repr(self).

property cutoff: float#

Distance cutoff (Å) used for Steinhardt and graph metrics.

property element_pool: list[str]#

A copy of the resolved element pool.

run(initial: Structure | None = None) OptimizationResult[source]#

Run n_restarts optimizations and return an OptimizationResult.

Each restart begins from an independently generated random gas-mode structure (or from initial if provided). All per-restart results are collected, sorted by objective value (highest first), and returned together in an OptimizationResult.

OptimizationResult is list-compatible: result[0] and result.best both return the highest-scoring structure, and for s in result iterates all restarts in rank order. Existing code that calls opt.run() and uses the return value as a single Structure should switch to opt.run().best or opt.run()[0].

A UserWarning is emitted when one or more restarts fail to produce a valid initial structure.

Parameters:

initial – Starting structure. When None (default), a random gas-mode structure is generated automatically for each restart.

Returns:

All per-restart structures sorted by objective value (highest first), plus summary metadata. Raises RuntimeError if every restart fails to produce a valid initial structure.

Return type:

OptimizationResult

Raises:

RuntimeError – When all restarts fail to produce a valid initial structure.

Examples

Best structure only:

result = opt.run()
print(result.best)          # highest-scoring structure
print(result[0])            # same — index 0 is always the best
print(result.summary())     # one-line diagnostic

All restarts:

result = opt.run()
for rank, s in enumerate(result, 1):
    print(f"rank {rank}: f={result.objective_scores[rank-1]:.4f}  {s}")
pasted._optimizer.parse_objective_spec(specs: list[str]) dict[str, float][source]#

Parse ["METRIC:WEIGHT", ...] into a weight dict.

Parameters:

specs – Each string must be of the form "METRIC:WEIGHT", e.g. ["H_atom:1.0", "Q6:-2.0"].

Return type:

dict[str, float]

Raises:

ValueError – On malformed strings or unknown metric names.

OptimizationResult#

class pasted._optimizer.OptimizationResult(all_structures: list[Structure] = <factory>, objective_scores: list[float] = <factory>, n_restarts_attempted: int = 0, method: str = 'annealing')[source]

Bases: object

Return value of StructureOptimizer.run().

Wraps all per-restart results and exposes the best structure as a first-class attribute. Behaves like a list[Structure] — indexing, iteration, len, and bool all work — so callers that only want the best result can access it without changing existing code:

result = opt.run()
best   = result.best        # highest-scoring Structure
best   = result[0]          # same — index 0 is always the best
for s in result:            # iterate all restarts, best first
    print(s.metrics["H_total"])
all_structures

All structures produced by each restart, sorted by objective value (highest first). all_structures[0] is always the best.

Type:

list[pasted._generator.Structure]

objective_scores

Scalar objective values corresponding to each entry in all_structures.

Type:

list[float]

n_restarts_attempted

Number of restarts that were actually run (may be less than n_restarts when initial-structure generation fails).

Type:

int

method

The optimization method used ("annealing", "basin_hopping", or "parallel_tempering").

Type:

str

Examples

Single-structure usage (backward-compatible):

result = opt.run()
result.best.to_xyz()      # best structure
result[0].to_xyz()        # same

All-restarts usage:

result = opt.run()
print(result.summary())
for rank, s in enumerate(result, 1):
    print(f"rank {rank}: H_total={s.metrics['H_total']:.3f}")
__iter__() Iterator[Structure][source]
__len__() int[source]
__repr__() str[source]

Return repr(self).

all_structures: list[Structure]
property best: Structure

The structure with the highest objective value.

method: str = 'annealing'
n_restarts_attempted: int = 0
objective_scores: list[float]
summary() str[source]

Return a human-readable one-line summary of the optimization run.

Returns:

E.g. "restarts=5  best_f=1.2294  worst_f=0.7823  method='annealing'".

Return type:

str