Scaling functions
A ScalingFunction defines how the data of one system size are rescaled for given values of the parameters. The fit searches the parameters that make the rescaled curves of all sizes collapse.
Presets
Three presets cover the usual scaling forms (see Finite-size scaling for which observable needs which):
| preset | x is rescaled to | y is rescaled to |
|---|---|---|
:x | ||
:xy (default) | ||
:xny |
Error bars are rescaled like the y values. p_names gives the parameters meaningful names, which appear in the output of the problem:
using ScalingCollapse
sf = ScalingFunction(:xny; p_names=["T_c", "nu", "gamma"])
sf.x_scale, sf.y_scale("(x - T_c) * L^(1/nu)", "y * L^(-gamma/nu)")As a shortcut, the keywords of the scaling function can be passed directly to ScalingProblem when no sf is given. Two names select the preset :x, three names :xy:
ScalingProblem(xs, ys, Ls; p_names=["T_c", "nu"]) # same as sf=ScalingFunction(:x; p_names=["T_c", "nu"])Fixing parameters
Pass a parameter's name and value to fix it:
sf = ScalingFunction(:xy; p_names=["T_c", "nu", "beta"], nu=1.0)
ScalingCollapse.scaled_p_names(sf)2-element Vector{String}:
"T_c"
"beta"A fixed parameter is not fitted. p_space, starting_ps, optimal_ps and optimal_ps_error only contain the free parameters, in the order given by ScalingCollapse.scaled_p_names(sf). Fixing parameters works for presets and custom scaling functions alike, e.g. nu=1.0 for the 2D Ising model, see the Ising example.
The relative form
With relative=true, the presets rescale x relative to the critical point, as
ScalingFunction(:x; p_names=["T_c", "nu"], relative=true).x_scale"(x - T_c)/T_c * L^(1/nu)"This was the default up to version 0.2. Both forms give the same parameters, but a window dx means different things: the window dx of the relative form is the window dx .* p1 of the absolute form. To reproduce an old fit, pass relative=true or multiply dx by the critical point. The absolute form also works for
Custom scaling functions
Any other scaling form is a function f(d, p1, p2, ...) that takes the data d::ScalingCollapse.Data of one system size and the values of all parameters (fixed ones included), and returns the rescaled data as a new ScalingCollapse.Data:
d.Lis the system size,d.xs,d.ys,d.esare the x values, y values and errors;return
ScalingCollapse.Data(d.L, new_xs, new_ys, new_es)and do not modifyd;rescale the errors like the y values.
As an example, consider strong corrections to scaling: the order parameter of the synthetic data below follows
using Random
x_c, ν, β, b, ω = 0.5, 1.2, 0.3, 2.0, 1.0
F(z) = 1 / (1 + exp(2z)) + 0.2
Ls = [8, 16, 32, 64]
xs = collect(range(0.2, 0.8; length=41))
rng = Xoshiro(7)
ys = [L^(-β / ν) * (1 + b * L^(-ω)) .* F.((xs .- x_c) .* L^(1 / ν)) .*
(1 .+ 0.002 .* randn(rng, length(xs))) for L in Ls]The preset :xy ignores the correction, and
sp_preset = ScalingProblem(xs, ys, Ls;
sf=ScalingFunction(:xy; p_names=["x_c", "nu", "beta"]),
p_space=[0.4:0.01:0.6, 0.5:0.1:2.0, 0.0:0.05:0.6],
dx=[-3.0, 3.0],
)ScalingProblem
Scaling function:
x -> (x - x_c) * L^(1/nu)
y -> y * L^(beta/nu)
Optimal parameters:
x_c = 0.5044776141090176 ± 0.004806636108938413
nu = 1.1361378710806007 ± 0.11502561536412848
beta = 0.41606139110150997 ± 0.02753302369347017
Optimization interval dx:
-3.0 < x < 3.0
Searched parameter space:
x_c ∈ 0.4:0.01:0.6
nu ∈ 0.5:0.1:2.0
beta ∈ 0.0:0.05:0.6A custom scaling function divides the correction out. We fit its amplitude b and fix the correction exponent omega; x_scale and y_scale describe the scaling for the output:
function corrected(d::ScalingCollapse.Data, x_c, nu, beta, b, omega)
xs = @. (d.xs - x_c) * d.L^(1 / nu)
c = d.L^(beta / nu) / (1 + b * d.L^(-omega))
return ScalingCollapse.Data(d.L, xs, d.ys .* c, d.es .* c)
end
sf = ScalingFunction(corrected;
p_names=["x_c", "nu", "beta", "b", "omega"],
omega=1.0,
x_scale="(x - x_c) * L^(1/nu)",
y_scale="y * L^(beta/nu) / (1 + b * L^(-omega))",
)
sp_custom = ScalingProblem(xs, ys, Ls;
sf,
p_space=[0.4:0.02:0.6, 0.5:0.1:2.0, 0.0:0.05:0.6, 0.0:0.5:4.0],
dx=[-3.0, 3.0],
)ScalingProblem
Scaling function:
x -> (x - x_c) * L^(1/nu)
y -> y * L^(beta/nu) / (1 + b * L^(-omega))
Fixed parameters:
omega = 1.0
Optimal parameters:
x_c = 0.5001223115185922 ± 0.0004916987402465885
nu = 1.1987045985869602 ± 0.011128163233774288
beta = 0.30059069700835167 ± 0.007694494515734365
b = 1.9748774331229584 ± 0.13798714245552557
Optimization interval dx:
-3.0 < x < 3.0
Searched parameter space:
x_c ∈ 0.4:0.02:0.6
nu ∈ 0.5:0.1:2.0
beta ∈ 0.0:0.05:0.6
b ∈ 0.0:0.5:4.0All four free parameters agree with the true values. Every additional parameter makes the scan more expensive (here starting_ps skips it, see Fitting.
Type stability
The scaling function is called many times during a fit. Broadcasting as above, or loops over preallocated vectors, keeps it fast. The parameters are passed as Float64.
Custom presets
To reuse a custom scaling form under a name, add a method to ScalingCollapse.handle_preset. It returns the function, the default parameter names and the display strings:
function ScalingCollapse.handle_preset(::Val{:corrected}; kwargs...)
p_names = get(kwargs, :p_names, ["x_c", "nu", "beta", "b", "omega"])
x_scale = "(x - $(p_names[1])) * L^(1/$(p_names[2]))"
y_scale = "y * L^($(p_names[3])/$(p_names[2])) / (1 + $(p_names[4]) * L^(-$(p_names[5])))"
return corrected, p_names, x_scale, y_scale
end
sf = ScalingFunction(:corrected; omega=1.0)
sf.y_scale"y * L^(beta/nu) / (1 + b * L^(-omega))"The new preset accepts p_names and fixed parameters like the built-in ones.