function-plot icon indicating copy to clipboard operation
function-plot copied to clipboard

Document return type of FunctionPlotDatum.fn functions

Open tomprogers opened this issue 2 years ago • 4 comments

The string syntax for specifying a fn is nice, but I prefer to implement my function as an actual Function. For example, instead of fn: 'x^2', I wish to use fn: (x) => Math.pow(x, 2).

One reason is that the string syntax doesn't support non-integer exponents, demanding that I convert to nth-root instead:

"power is not an integer, you should use nth-root instead, returning an empty interval"

Some experimentation and digging suggests that it is possible to provide an actual function rather than a string, but also that the expected return type is not a number. Nor is the argument a number; with the default sampler (which I think is "interval"), the argument is shaped like:

{ x: { lo: number, hi: number } }

However, none of the following work as a return value:

  • { y: { lo: number, hi: number } } (my first guess, which seemed reasonable, and seems like it must be right because the "interval1d" sampler appears to rely on this code to assess the return value)
  • [ number, number ]
  • number

And the docs (I think that's the relevant type) specify the return type as any, which seems like it can't be right; the prose only addresses the interpretation of the string type.

I'd recommend two changes to this documentation:

  • change the return type of this: (scope: FunctionPlotDatumScope) => any from "any" to some union type
  • add more prose entries that describe when each item in that union type is appropriate

tomprogers avatar May 19 '22 05:05 tomprogers

More digging reveals that the return value (in my scenario) should be shaped like one of your Intervals: { lo: number, hi: number }. However: it is not true that the return lo should be based on the sample's x.lo, but that it must be the lower of the two output values. That is: if the curve implemented curves downward, such that f(lo) > f(hi), then the return values must be swapped.

My curve is like that, so I'm doing this:

// `n` comes from a closure; I'm simultaneously plotting a single expression for multiple values

fn: ( sample ) => ({
	hi: n - Math.pow(sample.x.lo, 2),
	lo: n - Math.pow(sample.x.hi, 2)
})

A version guaranteed to work in any scenario would do the math on the low and high inputs and then sort them, like so:

fn: (sample) => {
	let [ lo, hi ] = [
		n - Math.pow(sample.x.lo, 2),
		n - Math.pow(sample.x.hi, 2)
	].sort()
	return { lo, hi }
}

tomprogers avatar May 19 '22 05:05 tomprogers

thanks for the report, would this be solved by the hull function? e.g.

import Interval from 'interval-arithmetic'
// ...
fn: (sample) => {
  const t = {
  	hi: n - Math.pow(sample.x.lo, 2),
  	lo: n - Math.pow(sample.x.hi, 2)
  }
  return Interval.hull(t, t)
}

mauriciopoppe avatar May 19 '22 17:05 mauriciopoppe

I'm not familiar with the term "hull," but the implementation of that function seems like it does what is needed. That said, it also seems like kind of a weird fit, since it requires two intervals, whereas what's needed here is just to "normalize" a single interval.

tomprogers avatar May 20 '22 00:05 tomprogers

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

stale[bot] avatar Sep 08 '22 23:09 stale[bot]