question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Composable Objective Functions

See original GitHub issue

State of the SimPEG

Currently we have a number of Regularization and DataMisfit classes which implement parts of the objective function. These are kept separate and explicitly combined in the InvProblem.

dmis = DataMisfit.l2_DataMisfit(survey)
reg = Regularization.Tikhonov(mesh)
invProb = InvProblem.BaseInvProblem(dmis, reg, opt)

In the Tikhonov regularization there are a number of fitting parameters: alpha_s, alpha_x, alpha_y, alpha_z, alpha_xx, alpha_yy, and alpha_zz these in turn combined with a linear operator (derivative or volume term): Wx, Wy, Wz, Wsmall, Wxx, Wyy, Wzz, and Wsmooth2 finally these are put together in a single operator W, which allows you combine in a single call:

weighted_residual = W * (mapping * (m - mref))
obj_function = 0.5 * r.dot(r)

Not only is this repeated seven times in a single class, the idea is also repeated throughout the DataMisfit classes as well! This means if we implement something like total variation, we have to duplicate that code in a lot of places.

Yikes.

What I would like to talk about in this issue is that we can break up the Regularization classes into a bunch of composable ObjectiveFunctions which can be added together and multiplied by scalars (the alpha). These should also be used by the DataMisfit classes.

Bugs in the current idea

  1. The alphas and Ws are super repetitive
  2. We cannot really do joint inversions (multiple physics, or properties)
  3. Higher order regularizations should be composable (e.g. Tikhonov)
  4. There is only a single beta applied in the inversion class, this is not extensible

Current Workarounds

There is a MultiRegularization class made by @sgkang in the SIP folder which allows multiple models to be regularized at the same time. You can get around a lot of it by doing InjectActiveCell maps or writing other custom Regularization classes. e.g. in joint inversion for volume and slowness: http://gif.eos.ubc.ca/sites/default/files/HeaglySEG2014.pdf

Other discussions

This was also brought up in the SimPEG meeting by @fourndo on October 4th, 2016 https://youtu.be/A_oy9w8J0q4?t=24m18s This goes through some of the group discussions on composable objective functions.

Some constraints

  • Keep the Tikhonov idea, although this may turn into a function rather than a class
  • Share code between the DataMisfit and Regularization
  • Make use of the properties library so ensure we can validate and pickle (think parallel)
  • Compose through addition and multiply scalars - or directives in the future (think beta)

Sketch at the implementation

dmis = Objective.DataMisfit_L2(survey)
reg = Objective.tikhonov(mesh)
beta = 1e-2
objfun = dmis + beta * reg
invProb = InvProblem.BaseInvProblem(objfun, opt)

You can see above that the functions would be composable through + and *. Here the tikhonov becomes a function rather than a class.

def tikhonov(mesh, alpha_s, alpha_x, etc):
    return (
        alpha_s * Smallness(mesh) + 
        alpha_x * Smoothness(mesh, direction='x') + 
        etc
    )

Similar to the Maps classes, there would be a CompositeObjective function. That can handle addition or multiplication with certain things.

Examples

Weighting objectives

For example, similar to the BetaEstimate_ByEig we could initialize the estimated weights for an objective function (through the eigenvalues).

beta1, beta2 = Directives.InitializeEstimatedWeights(
    [dmis1, dmis2, reg],  # These are the objective functions to weight
    [1.0, 2.0, 1e2]  # These would be the ratios
)
objfun = 1.0 * dmis1 + (beta1 * dmis2) + (beta2 * reg)

Note here that beta1 would be a InversionDirective that would need to be called on startup.

Block coordinate descent algorithm

Combined with the weighting we might want the ability to completely turn off the objective until later in the algorithm.

coords = Directives.CoordinateDescent(2)
objfun = (coords.block[0] * dmis1) + (coords.block[1] * dmis2) + reg

assert coords.block[0] == 1 and coords.block[0] == 0
coords.next()
assert coords.block[0] == 0 and coords.block[0] == 1

We probably want the ability for Directive.Float() to multiply with other objectives. That can come later…

Cross gradient

If we are weighting two models that should have similar contours, we could use a cross-gradient approach.

xgrad = Directives.CrossGradient(wires.sigma, wires.porosity)
objfun = dmis + beta * tikhonov(mesh) + xgrad

Other proposed changes

I would also propose the change that the .eval functions become .__call__. Similarly, the .evalDeriv and .eval2Deriv could become .deriv and .deriv2 which would be closer to the Maps class as well.

f = objfun(model)
g = objfun.deriv(model)
H = objfun.deriv2(model)

This would be a first step at moving towards a better minimize package that is interoperable with scipy and scikit-learn.

Summary

The above implementation would allow for composing multiple data objective functions and regularizations. We would work towards joint inversions and more complicated inversion schemes that are directed by reusable pieces. This would open the door for a lot of new things, and actually make a step towards reproducible joint inversions.

cc @simpeg/developers

Issue Analytics

  • State:closed
  • Created 7 years ago
  • Comments:9 (9 by maintainers)

github_iconTop GitHub Comments

1reaction
jcapriotcommented, Oct 31, 2016

So I had developed something very close to this for some of my own stuff that might be able to be extended for this. The basic idea is:

class ObjectiveFunction(object):
# these three ensure a NotImplemented Error is thrown if the extending class does not define
# f, d, and H
  def _f(self,x): raise NotImplementedError
  def _d(self,x): raise NotImplementedError
  def _H(self,x): raise NotImplementedError

  def __init__(self,func,d_func,H_func):
    self._f = func
    self._d = d_func
    self._H = H_func

  def f(self,x):
    return self._f(x)
  __call__ = f

  def d(self,x):
    return self._d(x)

  def H(self,x):
    return self._H(x)

  def __add__(self,other):
    if issubclass(ObjectiveFunction,type(other)):
      func = lambda x: self.f(x)+other.f(x)
      d_func = lambda x: self.d(x)+other.d(x)
      H_func = lambda x: self.H(x)+other.H(x)
      return ObjectiveFunction(func,d_func,H_func)
    else:
      return NotImplemented

  def __radd__(self,other):
    return self+other

  def __mul__(self,other):
    func = lambda x: other*self.f(x)
    d_func = lambda x: other*self.d(x)
    H_func = lambda x: other*self.H(x)
    return ObjectiveFunction(func,d_func,H_func)

  def __rmul__(self,other):
    return self*other

Classes that extend this ObjectiveFunction class would use the init method in their own class to set the information they need to evaluate, then overwrite the 3 functions f, d, and H

0reactions
lheagycommented, Jan 2, 2018

This closed with #511

Read more comments on GitHub >

github_iconTop Results From Across the Web

Composable Functions - Medium
A simple composable function. A single answer in the survey can be written in Compose as a function containing a Row with an...
Read more >
Thinking in Compose | Jetpack Compose - Android Developers
A simple composable function. Using Compose, you can build your user interface by defining a set of composable functions that take in data...
Read more >
Function composition (computer science) - Wikipedia
Not to be confused with object composition. In computer science, function composition is an act or mechanism to combine simple functions ... The...
Read more >
Composable functions and return types - Mobile Dev Notes
Composable functions that build UI don't return anything (they return Unit ) because they construct a representation of the UI state instead ...
Read more >
Composability — Toolz 0.10.0 documentation - Read the Docs
This standardized interface enables us to compose several general purpose functions to solve custom problems. Standard interfaces enable us to use many ...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found