From 0e1722e83f5390e12ff9162bc828598f12bc1319 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Villemot?= Date: Thu, 15 Jan 2026 11:15:12 +0100 Subject: [PATCH 1/4] feat: implement discrete-swap --- src/ParticlesMC.jl | 35 +++++++++++++++++++++++++++-------- src/moves.jl | 8 +++++++- 2 files changed, 34 insertions(+), 9 deletions(-) diff --git a/src/ParticlesMC.jl b/src/ParticlesMC.jl index 8120b1e..8f8dfd2 100644 --- a/src/ParticlesMC.jl +++ b/src/ParticlesMC.jl @@ -153,8 +153,8 @@ ParticlesMC implemented in Comonicon. error("Configuration file '$config' does not exist in the current path.") end list_type = get(system, "list_type", "LinkedList") # optional field - bonds = get(system, "bonds", nothing) - + bonds = get(system, "bonds", nothing) + # Extract simulation parameters sim = params["simulation"] steps = sim["steps"] @@ -175,7 +175,7 @@ ParticlesMC implemented in Comonicon. "list_type" => list_type, "bonds" => bonds, )) - else + else chains = load_chains(config, args=Dict( "temperature" => [temperature], "density" => [density], @@ -214,8 +214,27 @@ ParticlesMC implemented in Comonicon. else error("Unsupported policy: $policy for action: $action") end + elseif action == "DiscreteSwap" + if "species" in keys(parameters) + species = parameters["species"] + if length(species) != 2 || eltype(species) != Int + error("'species' for action: $action must be an array of two ints") + end + else + error("Missing parameter 'species' for action: $action") + end + + # Use a system to initialize (chains[1]) + # This is because the action needs the number of particles for each species + action_obj = DiscreteSwap(species, chains[1]) + param_obj = Vector{Float64}() + if policy == "DoubleUniform" + policy_obj = DoubleUniform() + else + error("Unsupported policy: $policy for action: $action") + end else - error("Unsupported action: $action") + error("Unsupported action: $action") end # Build move move_obj = Move(action_obj, policy_obj, param_obj, prob) @@ -257,15 +276,15 @@ ParticlesMC implemented in Comonicon. else error("Unsupported output algorithm: $alg") end - push!(algorithm_list, algorithm) + push!(algorithm_list, algorithm) end M=1 path = joinpath(output_path) simulation = Simulation(chains, algorithm_list, steps; path=path, verbose=true) - + # Run the simulation run!(simulation) -end +end -end \ No newline at end of file +end diff --git a/src/moves.jl b/src/moves.jl index b357eda..f3e25ca 100644 --- a/src/moves.jl +++ b/src/moves.jl @@ -146,6 +146,12 @@ mutable struct DiscreteSwap <: Action δe::Float64 end +function DiscreteSwap(species::Vector{Int}, system::Atoms) + particles_per_species_1 = count(i -> (i == species[1]), system.species) + particles_per_species_2 = count(i -> (i == species[2]), system.species) + return DiscreteSwap(1, 1, (species[1], species[2]), (particles_per_species_1, particles_per_species_2), 0.0) +end + """ Swap the species identifiers of particles `i` and `j` and return the total pre- and post-swap energies for those two particles. @@ -349,4 +355,4 @@ function Arianna.sample_action!(action::MoleculeFlip, ::DoubleUniform, parameter action.i, action.j = i, j end -############################################################################### \ No newline at end of file +############################################################################### From 4a4c0c7626bb6f4f4680e62956ada33ea1727737 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Villemot?= Date: Fri, 16 Jan 2026 09:19:22 +0100 Subject: [PATCH 2/4] feat: update validation to include discrete swaps --- validation/lj-mixture/README.md | 6 +-- validation/lj-mixture/calculated-energies.csv | 48 +++++++++--------- validation/lj-mixture/correlation-plot.jpeg | Bin 21850 -> 21846 bytes validation/lj-mixture/run-validation.py | 19 +++++-- 4 files changed, 41 insertions(+), 32 deletions(-) diff --git a/validation/lj-mixture/README.md b/validation/lj-mixture/README.md index 82dea55..4e89d3b 100644 --- a/validation/lj-mixture/README.md +++ b/validation/lj-mixture/README.md @@ -1,14 +1,14 @@ # Binary mixture of LJ particles -This directory contains scripts to run a simple simulation of a binary mixture of Lennard-Jones particles, in 3D. +This directory contains scripts to run a simple simulation of a binary mixture of Lennard-Jones particles, in 3D. Swap Monte Carlo moves are attempted o The average energy is recorded, for different conditions (temperature, density, ratio between the two species), and compared to published results. -Reproducing the published results validates the implementation of the MC scheme, the LJ potential, and interaction counting. +Reproducing the published results validates the implementation of the MC scheme, the LJ potential, the discrete swap algorithm, and energy evaluation. ## How to run -The `particlemc` exectuable should be in the PATH env variable, otherwise a specific path can +The `particlemc` executable should be in the PATH env variable, otherwise a specific path can be specified at the top of the run-validation.py script. The `run-validation.py` script is meant to be run with [uv](https://docs.astral.sh/uv/), with the following command: diff --git a/validation/lj-mixture/calculated-energies.csv b/validation/lj-mixture/calculated-energies.csv index 05385a6..1c4d964 100644 --- a/validation/lj-mixture/calculated-energies.csv +++ b/validation/lj-mixture/calculated-energies.csv @@ -1,24 +1,24 @@ -,t,x,density,u,energy,energy_err,lr_correction,acceptance_rate -0,1.2183,0.5,0.8,-6.991912,-6.824332775509206,0.007332089000306406,-151.9222906615277,0.523858 -1,1.2183,0.5,0.7,-6.28399,-6.15696393793348,0.0115122047621387,-132.93200432883674,0.605872 -2,1.2183,0.5,0.65,-5.86756,-5.747027114561171,0.008533981399021708,-123.43686116249125,0.643768 -3,1.5096,0.25,0.8,-5.87326,-5.728584068606758,0.021466385837235483,-127.1877273361547,0.580572 -4,1.5096,0.25,0.7,-5.2830446,-5.1486481236643895,0.015199454304553882,-111.28926141913534,0.65148 -5,1.5096,0.25,0.6,-4.57915,-4.457067486522425,0.012901052999909882,-95.39079550211606,0.713526 -6,1.5096,0.25,0.5,-3.84601,-3.7550796383339944,0.016097072216878654,-79.4923295850967,0.767107 -7,1.5096,0.25,0.4,-3.145625,-3.031679704426388,0.01957993121178323,-63.59386366807736,0.812887 -8,1.5096,0.25,0.3,-2.446133,-2.29186093973726,0.019961432466160035,-47.69539775105803,0.853689 -9,1.5096,0.25,0.2,-1.71833,-1.5204237232517432,0.018705478102023994,-31.796931834038674,0.897772 -10,1.5096,0.25,0.1,-0.88341,-0.7731743940092469,0.012321114326961577,-15.898465917019347,0.94422 -11,1.5096,0.75,0.8,-7.528813,-7.387557821039766,0.019429067147871005,-178.9234450957788,0.515488 -12,1.5096,0.75,0.7,-6.89008,-6.705393398859641,0.024860213510585486,-156.55801445880638,0.598998 -13,1.5096,0.75,0.6,-6.0058145,-5.844619979911088,0.01211174105592481,-134.19258382183412,0.674251 -14,1.5096,0.75,0.55,-5.52616,-5.422084348147788,0.005698415488936215,-123.00986850334797,0.708598 -15,1.6438,0.5,0.8,-6.54401,-6.36678624267275,0.008473648040505592,-151.9222906615277,0.557943 -16,1.6438,0.5,0.7,-5.95657,-5.844425928860567,0.013831459037555685,-132.93200432883674,0.634995 -17,1.6438,0.5,0.6,-5.18836,-5.085670690270235,0.010455755569991681,-113.94171799614583,0.702152 -18,1.6438,0.5,0.5,-4.35859,-4.21187686179021,0.010561762660606716,-94.95143166345483,0.758437 -19,1.6438,0.5,0.4,-3.549019,-3.4182878823413763,0.011275107483718705,-75.96114533076386,0.807701 -20,1.6438,0.5,0.3,-2.768327,-2.575420052801317,0.010850764609473712,-56.97085899807291,0.853085 -21,1.6438,0.5,0.2,-1.936931,-1.7475874342217939,0.01920265179952064,-37.98057266538193,0.89591 -22,1.6438,0.5,0.1,-1.004111,-0.8560035870798322,0.012956204273870288,-18.99028633269097,0.942764 +,t,x,density,u,energy,energy_err,lr_correction,acceptance_rate_displacement,acceptance_rate_swap +0,1.2183,0.5,0.8,-6.991912,-6.840534104086828,0.017637447754306167,-151.9222906615277,0.524164659893668,0.39982694488095 +1,1.2183,0.5,0.7,-6.28399,-6.165202896818967,0.016206321890262023,-132.93200432883674,0.606704734205417,0.496359875082052 +2,1.2183,0.5,0.65,-5.86756,-5.755000382698143,0.013358234226850594,-123.43686116249125,0.645592770725351,0.539404849521612 +3,1.5096,0.25,0.8,-5.87326,-5.770095515865939,0.01683749104035944,-127.1877273361547,0.581054728757668,0.45646768643208085 +4,1.5096,0.25,0.7,-5.2830446,-5.18929850304146,0.013574981514565761,-111.28926141913534,0.6520411271727,0.5359636385336065 +5,1.5096,0.25,0.6,-4.57915,-4.496678165866352,0.004256448674720936,-95.39079550211606,0.71337500305741,0.6050265550096473 +6,1.5096,0.25,0.5,-3.84601,-3.744194994789512,0.009028764596754972,-79.4923295850967,0.768620741027334,0.6672667236886599 +7,1.5096,0.25,0.4,-3.145625,-3.0625659840628465,0.009621436608541251,-63.59386366807736,0.812516259864317,0.7167863465478488 +8,1.5096,0.25,0.3,-2.446133,-2.315936149881792,0.007309758603592841,-47.69539775105803,0.85386912504697,0.766972331072345 +9,1.5096,0.25,0.2,-1.71833,-1.5634718658838844,0.013177941604522733,-31.796931834038674,0.897846916017939,0.8230561136196368 +10,1.5096,0.25,0.1,-0.88341,-0.7540551691012838,0.016305680822504467,-15.898465917019347,0.946107305098426,0.8910946233564736 +11,1.5096,0.75,0.8,-7.528813,-7.337683683940237,0.010453572023241963,-178.9234450957788,0.513666068526016,0.4023630974877171 +12,1.5096,0.75,0.7,-6.89008,-6.74127765861963,0.013307090789325909,-156.55801445880638,0.59955150569123,0.4993833668171782 +13,1.5096,0.75,0.6,-6.0058145,-5.886840679436459,0.014665817674222618,-134.19258382183412,0.675006170410048,0.5875420205677003 +14,1.5096,0.75,0.55,-5.52616,-5.410653702890696,0.014852028782687345,-123.00986850334797,0.70719013979592,0.6243311519105683 +15,1.6438,0.5,0.8,-6.54401,-6.394357885128574,0.019019860433670554,-151.9222906615277,0.557749479128449,0.4403258210172458 +16,1.6438,0.5,0.7,-5.95657,-5.836950816506536,0.013006657681474675,-132.93200432883674,0.633923469126825,0.5291707278260697 +17,1.6438,0.5,0.6,-5.18836,-5.103247816622887,0.009429572650158194,-113.94171799614583,0.702751891703188,0.6058918306048973 +18,1.6438,0.5,0.5,-4.35859,-4.2967131610134635,0.012212677851235598,-94.95143166345483,0.757340564386839,0.6687187953772402 +19,1.6438,0.5,0.4,-3.549019,-3.441614177830049,0.008689906174574758,-75.96114533076386,0.80890073311142,0.7258170389672389 +20,1.6438,0.5,0.3,-2.768327,-2.6187924148641315,0.01460481900316066,-56.97085899807291,0.85185679312116,0.7742326895152467 +21,1.6438,0.5,0.2,-1.936931,-1.7582716394839677,0.016702400788528176,-37.98057266538193,0.896708447569303,0.8260597139617687 +22,1.6438,0.5,0.1,-1.004111,-0.8719412344268423,0.018406946799003905,-18.99028633269097,0.943625799651788,0.8937103415352177 diff --git a/validation/lj-mixture/correlation-plot.jpeg b/validation/lj-mixture/correlation-plot.jpeg index abfe4c1b4a743e8271f65a8a21c377189aa7d415..9bed75527685b725834543a92bd710614068a4b5 100644 GIT binary patch delta 14385 zcmcI~bxfRX_vQl>X|Y0~xD+oA#oe{IGe{}!?u7x0J4}J%?pEBTxKkVkcXuti^S
e2q_x}P~w?w?xSL@*!y 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 AVgLXD delta 14524 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 None: [[simulation.move]] action = "Displacement" - probability = 1.0 + probability = 0.9 policy = "SimpleGaussian" parameters = {{sigma = 0.05}} + [[simulation.move]] + action = "DiscreteSwap" + probability = 0.1 + policy = "DoubleUniform" + parameters = {{species = [1, 2]}} + [[simulation.output]] algorithm = "StoreCallbacks" callbacks = ["energy", "acceptance"] @@ -168,9 +174,11 @@ def run_simulations(output_path: str) -> None: # Remove the first half as equilibration, just to be sure energies = energies[int(len(energies) / 2) :] - acceptance_rate = np.array( - pd.read_csv(f"{workdir}/acceptance.dat", sep="\\s+", names=["i", "a"])["a"] - )[-1] + df_acceptance_rates = pd.read_csv( + f"{workdir}/acceptance.dat", sep="\\s+", names=["i", "move", "swap"] + ) + displacement_acceptance = float(df_acceptance_rates["move"].iloc[-1][1:-2]) + swap_acceptance = float(df_acceptance_rates["swap"].iloc[-1][:-1]) # Compute long-range corrections from the cutoff # Formula from Gromacs https://manual.gromacs.org/current/reference-manual/functions/long-range-vdw.html @@ -195,7 +203,8 @@ def run_simulations(output_path: str) -> None: "energy": np.mean(energies), "energy_err": np.std(energies) / np.sqrt(len(energies)), "lr_correction": lr_correction, - "acceptance_rate": json.loads(acceptance_rate)[0], + "acceptance_rate_displacement": displacement_acceptance, + "acceptance_rate_swap": swap_acceptance, } ) From a892978ec741a61f284ce58aad386173f764277c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Villemot?= Date: Fri, 16 Jan 2026 09:27:45 +0100 Subject: [PATCH 3/4] docs: update main README to document discrete swaps --- README.md | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index ff5c0e8..125cdbd 100644 --- a/README.md +++ b/README.md @@ -93,7 +93,7 @@ rcut = 2.5 [simulation] type = "Metropolis" -steps = 500 +steps = 500 seed = 10 parallel = false output_path = "./" @@ -120,7 +120,21 @@ This example defines a minimal Monte Carlo simulation setup: - The `[[simulation.move]]` section describes the Monte Carlo move to use: a displacement move with probability 1.0, guided by a simple Gaussian policy with a standard deviation (`sigma`) of 0.05. - The `[[simulation.output]]` section configures the output: trajectories will be stored every 50 steps in the XYZ format. -By executing `particlesmc params.toml` you will run a basic Metropolis Monte Carlo simulation of particles interacting via the Lennard-Jones potential, using displacement moves, and periodically saving the system's trajectory. +By executing `particlesmc params.toml** you will run a basic Metropolis Monte Carlo simulation of particles interacting via the Lennard-Jones potential, using displacement moves, and periodically saving the system's trajectory. + +**Extending beyond this simple example:** + +More models and moves are available. + +For instance, in a simulation that contains multiple species, __DiscreteSwap__ moves can be added to swap species between to random particles: +```toml +[[simulation.move]] +action = "DiscreteSwap" +probability = 0.1 +policy = "DoubleUniform" +parameters = {species = [1, 2]} +``` +is a move that will be attempted 10% of the time (probability = 0.1). It will select a random particle _i_ of species _1_, a random particle _j_ of species _2_, and will attempt to transform _i_ into species _2_, and _j_ into species _1_. ## Contributing From 86f75657710114e623aa3422c9a43d2609a548da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Villemot?= Date: Fri, 16 Jan 2026 09:47:11 +0100 Subject: [PATCH 4/4] docs: typos --- validation/lj-mixture/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/validation/lj-mixture/README.md b/validation/lj-mixture/README.md index 4e89d3b..16e52e8 100644 --- a/validation/lj-mixture/README.md +++ b/validation/lj-mixture/README.md @@ -1,6 +1,6 @@ # Binary mixture of LJ particles -This directory contains scripts to run a simple simulation of a binary mixture of Lennard-Jones particles, in 3D. Swap Monte Carlo moves are attempted o +This directory contains scripts to run a simple simulation of a binary mixture of Lennard-Jones particles, in 3D. Swap Monte Carlo moves are attempted on top of normal displacements. The average energy is recorded, for different conditions (temperature, density, ratio between the two species), and compared to published results.