NAME

Physics::Ellipsometry::VASE::Parameter - Named parameters with bounds, vary/fix control, and internal transformations for model fitting

SYNOPSIS

use PDL;
use Physics::Ellipsometry::VASE;
use Physics::Ellipsometry::VASE::Parameter qw(
    param params_to_pdl pdl_to_params make_fit_model get_values
);

# Define model parameters with physical constraints
my @params = (
    param(name => 'thickness', value => 100.0,
          min  => 0.0, max => 500.0),
    param(name => 'n_cauchy_A', value => 2.1,
          min  => 1.0, max => 4.0),
    param(name => 'n_cauchy_B', value => 0.01,
          min  => 0.0, max => 1.0),
    param(name => 'angle_offset', value => 0.0, vary => 0),  # fixed
);

# Convert varying parameters to a PDL for fitting
my $p0 = params_to_pdl(\@params);   # 3-element piddle (angle_offset excluded)

# After fitting, update parameter values from the fitted PDL
pdl_to_params(\@params, $fitted_pdl);

# Read back all values (fixed and varying)
my @values = get_values(\@params);
printf "thickness = %.2f nm\n", $values[0];

DESCRIPTION

Levenberg-Marquardt fitting operates on an unconstrained vector of real numbers, but physical model parameters often have hard constraints: thicknesses cannot be negative, refractive indices must stay within sensible ranges, and some parameters (e.g., a known substrate angle) should be held fixed during the fit.

Physics::Ellipsometry::VASE::Parameter provides a lightweight parameter object that addresses these needs:

Named parameters

Each parameter carries a human-readable name for identification in reports and diagnostics.

Bounds enforcement via smooth transformations

Rather than clamping (which creates discontinuous derivatives and confuses gradient-based optimizers), bounded parameters are mapped to an unbounded internal space using invertible transformations:

Two-sided bounds (min and max) — logit transform:
internal = ln( (v − min) / (max − v) )
Lower bound only — log transform:
internal = ln( v − min )
Upper bound only — negative log transform:
internal = −ln( max − v )

The optimizer works in internal space where all parameters are unconstrained. The transforms are smooth (C∞), so the Jacobian computed by finite differences remains well-behaved near the bounds.

Vary/fix control

Parameters with vary => 0 are held at their current value and excluded from the fitted PDL vector. This lets you freeze known quantities (e.g., substrate optical constants) without rewriting the model function.

Scaling

An optional scale factor adjusts the internal representation for numerical conditioning. This is useful when parameters span very different orders of magnitude.

FUNCTIONS

param

my $p = param(
    name  => 'thickness',
    value => 100.0,
    min   => 0.0,
    max   => 500.0,
    vary  => 1,
    scale => 1.0,
);

Creates a parameter hashref. All keyword arguments are optional:

name — descriptive name (default: 'unnamed')
value — current value (default: 0.0)
min — lower bound, or undef for unbounded below
max — upper bound, or undef for unbounded above
vary — 1 to vary during fitting, 0 to hold fixed (default: 1)
scale — internal scaling factor (default: 1.0)
# Unbounded parameter
param(name => 'offset', value => 0.5)

# Lower-bounded only (e.g., thickness ≥ 0)
param(name => 'thickness', value => 50.0, min => 0.0)

# Fixed parameter (excluded from fit)
param(name => 'angle', value => 70.0, vary => 0)

params_to_pdl

my $pdl = params_to_pdl(\@params);

Extracts the varying parameters from the list, transforms each to its internal (unbounded) representation, and returns a 1-D PDL piddle suitable for passing to the fitter. Fixed parameters are skipped.

pdl_to_params

pdl_to_params(\@params, $fitted_pdl);

The inverse of "params_to_pdl". Takes a fitted PDL of internal values and maps each back to physical space, updating the value field of each varying parameter in-place. Fixed parameters are unchanged.

get_values

my @vals = get_values(\@params);

Returns the current value of every parameter (both fixed and varying), preserving order. Convenient for building a full parameter PDL or for printing a summary.

make_fit_model

my $wrapped = make_fit_model(\@params, \&full_model);

Creates a closure that bridges the gap between the fitter (which sees only varying parameters in internal space) and the user's model function (which expects the full physical parameter vector).

The returned closure has the signature expected by "fit" in Physics::Ellipsometry::VASE:

$wrapped->($vary_pdl, $x_data)  →  $y_pdl

Internally it:

  1. Transforms each element of $vary_pdl from internal to physical space using the inverse bound transform.

  2. Inserts fixed parameter values at the correct positions.

  3. Calls $full_model->($all_params_pdl, $x_data).

COMPLETE FITTING EXAMPLE

use PDL;
use Physics::Ellipsometry::VASE;
use Physics::Ellipsometry::VASE::Parameter qw(
    param params_to_pdl pdl_to_params make_fit_model get_values
);

# 1. Define parameters with physical bounds
my @params = (
    param(name => 'd',   value => 80.0,  min => 0, max => 500),
    param(name => 'A',   value => 2.0,   min => 1, max => 4),
    param(name => 'B',   value => 0.01,  min => 0, max => 1),
    param(name => 'C',   value => 0.0,   vary => 0),  # fix at zero
);

# 2. Write the model in terms of a full parameter vector
sub my_model {
    my ($all_p, $x) = @_;
    my ($d, $A, $B, $C) = ($all_p->at(0), $all_p->at(1),
                            $all_p->at(2), $all_p->at(3));
    # ... compute psi, delta using TMM ...
    return cat($psi, $delta)->flat;
}

# 3. Wrap the model to handle bounds and fixed params
my $wrapped = make_fit_model(\@params, \&my_model);

# 4. Fit — optimizer sees only 3 unconstrained variables
my $vase = Physics::Ellipsometry::VASE->new(layers => 1);
$vase->load_data('sample.dat');
$vase->set_model($wrapped);

my $p0 = params_to_pdl(\@params);
my $fitted = $vase->fit($p0);

# 5. Map fitted values back to physical space
pdl_to_params(\@params, $fitted);
for my $p (@params) {
    printf "%-12s = %8.4f  %s\n",
           $p->{name}, $p->{value},
           $p->{vary} ? '' : '(fixed)';
}

SEE ALSO

Physics::Ellipsometry::VASE, Physics::Ellipsometry::VASE::Optimizer