Skip to main content
edited body
Source Link
Henrik Schumacher
  • 112.9k
  • 7
  • 197
  • 339

The following implementation is basically Newton's mwthodmethod. It assumes that towards the end of the optimization, the relation between data points and closest reference points does not change anymore. In that regime, the objective function is quadratic and Newton converges in a single step. The Newton search direction is also a good choice outside of this regime, and so we can employ Armijo line search to globalize Newton's method. (Newton's method is notorious for wild behavior outsize a basin of attraction. One of the reasons (when the Hessian is positive-definite) is that the size of the step is typically too large.)

The following implementation is basically Newton's mwthod. It assumes that towards the end of the optimization, the relation between data points and closest reference points does not change anymore. In that regime, the objective function is quadratic and Newton converges in a single step. The Newton search direction is also a good choice outside of this regime, and so we can employ Armijo line search to globalize Newton's method. (Newton's method is notorious for wild behavior outsize a basin of attraction. One of the reasons (when the Hessian is positive-definite) is that the size of the step is typically too large.)

The following implementation is basically Newton's method. It assumes that towards the end of the optimization, the relation between data points and closest reference points does not change anymore. In that regime, the objective function is quadratic and Newton converges in a single step. The Newton search direction is also a good choice outside of this regime, and so we can employ Armijo line search to globalize Newton's method. (Newton's method is notorious for wild behavior outsize a basin of attraction. One of the reasons (when the Hessian is positive-definite) is that the size of the step is typically too large.)

deleted 20 characters in body
Source Link
Henrik Schumacher
  • 112.9k
  • 7
  • 197
  • 339
ClearAll[findShift]; findShift[data_, reference_, {a_, b_, n_Integer}] := Module[{shift, d, h, Y, nf}, (* n+1 = the number of shifting operations per dimension.*) (* a = leftmost shift in each direction*) (* b rightmost shift in each direction*) (* The step size.*) h = (b - a)/n; (*Build only one nearest function for all shifting operations.*) nf = Nearest[data -> "Distance"]; d = Dimensions[data][[-1]]; (*a vector to store the shifts*) shift = ConstantArray[0., d]; Y = reference; Do[ Y[[All, direction]] -= (a - h); shift[[direction]] = MinimalBy[ (*You can parallize this loop if you want.*) Table[ (*The trick is to shift the reference,  not the data! *) (*This allows us to reuse the Nearest \ function nf.*) Y[[All, direction]] -= h; {s, Total[nf[Y]^2, 2]} , {s, a, b, h} ], Last ][[1, 1]]; (*Apply optimal shift in current direction.*) Y[[All, direction]] += b - shift[[direction]]; , {direction, 1, d}]; (*Finally, apply the shifts to the data once.*) data + ConstantArray[shift, Length[data]] ] 
ClearAll[findShift]; findShift[data_, reference_, {a_, b_, n_Integer}] := Module[{shift, d, h, Y, nf}, (* n+1 = the number of shifting operations per dimension.*) (* a = leftmost shift in each direction*) (* b rightmost shift in each direction*) (* The step size.*) h = (b - a)/n; (*Build only one nearest function for all shifting operations.*) nf = Nearest[data -> "Distance"]; d = Dimensions[data][[-1]]; (*a vector to store the shifts*) shift = ConstantArray[0., d]; Y = reference; Do[ Y[[All, direction]] -= (a - h); shift[[direction]] = MinimalBy[ (*You can parallize this loop if you want.*) Table[ (*The trick is to shift the reference,  not the data! *) (*This allows us to reuse the Nearest \ function nf.*) Y[[All, direction]] -= h; {s, Total[nf[Y]^2, 2]} , {s, a, b, h} ], Last ][[1, 1]]; (*Apply optimal shift in current direction.*) Y[[All, direction]] += b - shift[[direction]]; , {direction, 1, d}]; (*Finally, apply the shifts to the data once.*) data + ConstantArray[shift, Length[data]] ] 
ClearAll[findShift]; findShift[data_, reference_, {a_, b_, n_Integer}] := Module[{shift, d, h, Y, nf}, (* n+1 = the number of shifting operations per dimension.*) (* a = leftmost shift in each direction*) (* b rightmost shift in each direction*) (* The step size.*) h = (b - a)/n; (*Build only one nearest function for all shifting operations.*) nf = Nearest[data -> "Distance"]; d = Dimensions[data][[-1]]; (*a vector to store the shifts*) shift = ConstantArray[0., d]; Y = reference; Do[ Y[[All, direction]] -= (a - h); shift[[direction]] = MinimalBy[ (*You can parallize this loop if you want.*) Table[ (*The trick is to shift the reference, not the data! *) (*This allows us to reuse the Nearest function nf.*) Y[[All, direction]] -= h; {s, Total[nf[Y]^2, 2]} , {s, a, b, h} ], Last ][[1, 1]]; (*Apply optimal shift in current direction.*) Y[[All, direction]] += b - shift[[direction]]; , {direction, 1, d}]; (*Finally, apply the shifts to the data once.*) data + ConstantArray[shift, Length[data]] ] 
added 2092 characters in body
Source Link
Henrik Schumacher
  • 112.9k
  • 7
  • 197
  • 339

Okay, after I have seen that Newton's method does not work well here, here my reformulation of OP's algorithm. This implementation finds the same shifts, but it is far more efficient for large datasets.

ClearAll[findShift]; findShift[data_, reference_, {a_, b_, n_Integer}] := Module[{shift, d, h, Y, nf}, (* n+1 = the number of shifting operations per dimension.*) (* a = leftmost shift in each direction*) (* b rightmost shift in each direction*) (* The step size.*) h = (b - a)/n; (*Build only one nearest function for all shifting operations.*) nf = Nearest[data -> "Distance"]; d = Dimensions[data][[-1]]; (*a vector to store the shifts*) shift = ConstantArray[0., d]; Y = reference; Do[ Y[[All, direction]] -= (a - h); shift[[direction]] = MinimalBy[ (*You can parallize this loop if you want.*) Table[ (*The trick is to shift the reference, not the data! *) (*This allows us to reuse the Nearest \ function nf.*) Y[[All, direction]] -= h; {s, Total[nf[Y]^2, 2]} , {s, a, b, h} ], Last ][[1, 1]]; (*Apply optimal shift in current direction.*) Y[[All, direction]] += b - shift[[direction]]; , {direction, 1, d}]; (*Finally, apply the shifts to the data once.*) data + ConstantArray[shift, Length[data]] ] 

And here is a a usage example.

reference = RandomReal[{-1, 1}, {100, 3}]; data1 = RandomReal[{-1, 1}, {100000, 3}]; data1shifted2 = findShift[data1, reference, {-1., 1., 20}]; // AbsoluteTiming // First 

0.027269

OP's code requires 5.87231 seconds for the same task on my machine. So this is an improvement of factor 200.

Newton's method is not very effective for this type of problem since there are many local minima. I keep the following only as reference.

Okay, after I have seen that Newton's method does not work well here, here my reformulation of OP's algorithm. This implementation finds the same shifts, but it is far more efficient for large datasets.

ClearAll[findShift]; findShift[data_, reference_, {a_, b_, n_Integer}] := Module[{shift, d, h, Y, nf}, (* n+1 = the number of shifting operations per dimension.*) (* a = leftmost shift in each direction*) (* b rightmost shift in each direction*) (* The step size.*) h = (b - a)/n; (*Build only one nearest function for all shifting operations.*) nf = Nearest[data -> "Distance"]; d = Dimensions[data][[-1]]; (*a vector to store the shifts*) shift = ConstantArray[0., d]; Y = reference; Do[ Y[[All, direction]] -= (a - h); shift[[direction]] = MinimalBy[ (*You can parallize this loop if you want.*) Table[ (*The trick is to shift the reference, not the data! *) (*This allows us to reuse the Nearest \ function nf.*) Y[[All, direction]] -= h; {s, Total[nf[Y]^2, 2]} , {s, a, b, h} ], Last ][[1, 1]]; (*Apply optimal shift in current direction.*) Y[[All, direction]] += b - shift[[direction]]; , {direction, 1, d}]; (*Finally, apply the shifts to the data once.*) data + ConstantArray[shift, Length[data]] ] 

And here is a a usage example.

reference = RandomReal[{-1, 1}, {100, 3}]; data1 = RandomReal[{-1, 1}, {100000, 3}]; data1shifted2 = findShift[data1, reference, {-1., 1., 20}]; // AbsoluteTiming // First 

0.027269

OP's code requires 5.87231 seconds for the same task on my machine. So this is an improvement of factor 200.

Newton's method is not very effective for this type of problem since there are many local minima. I keep the following only as reference.

added 2076 characters in body
Source Link
Henrik Schumacher
  • 112.9k
  • 7
  • 197
  • 339
Loading
added 59 characters in body
Source Link
Henrik Schumacher
  • 112.9k
  • 7
  • 197
  • 339
Loading
Source Link
Henrik Schumacher
  • 112.9k
  • 7
  • 197
  • 339
Loading