[Ifeffit] Hard restraints?

Matt Newville newville at cars.uchicago.edu
Wed Feb 9 10:57:02 CST 2005


Hi Scott, Theanne,

> .... We can't just put an abs() around the def'd values,
> because that means our constraints based on stoichiometry and
> the relative number of sites are being applied incorrectly. A
> restraint doesn't really seem appropriate either.
>
> Does anyone have an idea of how to deal with this?

In a simplified case, you'd like to constrain two coordination
numbers to be positive _and_ to add up to some value, right?  
  guess n1_var = 5
  def   n1     = abs(n1_var)
  def   n2     = 12 - n1

or, if you're worried about n1 > 12, n2<0, you could do: 
  guess n1_var = 5
  def   n1     = max(0,min(n1_var,12))
  def   n2     = 12 - n1

Is that good enough, or am I not seeing the whole complicated
picture?

> If no one does, perhaps there should be an ifeffit option for
> a "hard restraint." This would be something like the max and
> min functions, except that it would operate by putting a huge
> penalty to the chi-square when outside the range. Just to be
> clear, it would work something like this: y has been def'd to
> x + z. Then y is also given a hard restraint that it cannot be
> less than 0. The fit would then generate a positive value of y
> that was not less than 0 by varying x and z in such a way that
> it is still true that y = x + z.

I think this is possible without a change to the code.  You can
add a restraint penalty when some value is outside a range
[lo_val,hi_val].  It's a bit scary looking, but this will do it:

  lo_val   = 0.7    ## lower bound
  up_val   = 1.0    ## upper bound
  scale    = 1.000  ## scale can be increased to weight this restraint
  target   = 0.8    ## this could be a variable, say S02

  ## use penalty as a restraint in feffit()
  def  penalty  = (max((target-lo_val), abs(target-lo_val)) - 
                   min((target-up_val),-abs(target-up_val)) + 
                   lo_val - up_val)*scale

This penalty can be used as a restraint, and will be 0 when the 
target value is between the lower and upper bound.

To set a penalty only for going below the lower bound, it's just
  def penalty = max((target-lo_val),abs(target-lo_val))-(target- lo_val)

and if the lower bound is 0, it's even simpler:
  def penalty = max(target,abs(target))-target

Even that is scary enough, and the whole thing may suggest a
change to the code to make a simple function that provides a
restraint penalty when a variable is outside some user-selected
bounds, perhaps as
    bound(x, lo_val, hi_val)  # not implemented!

Is this way of setting a penalty what you had in mind, or am I
missing the point?


--Matt

PS: 

##########
# To visualize this restraint, an array of penalties can be 
# plotted v. an array of targe values:
  lo_val   = 0.7
  up_val   = 1.0
  scale    = 1.000

  # array of target values
  m.vals = range(-1,3,0.05)

  m.lo_bound = scale*(m.vals-up_val)
  m.up_bound = scale*(m.vals-lo_val)

  m.lo_penalty =  max(m.up_bound,  abs(m.up_bound)) - m.up_bound
  m.up_penalty = -min(m.lo_bound, -abs(m.lo_bound)) + m.lo_bound

  m.penalty =  (max(m.up_bound,  abs(m.up_bound)) 
              - min(m.lo_bound, -abs(m.lo_bound)) + scale*(lo_val - up_val))

  newplot m.vals, m.penalty, style=linespoints2
  plot m.vals, m.lo_penalty

##########





More information about the Ifeffit mailing list