Merge pull request #529 from quantified-uncertainty/samplesMap
SamplesMap() and toInternalSampleArray() functions
This commit is contained in:
		
						commit
						2b48e2bb53
					
				| 
						 | 
				
			
			@ -17,6 +17,10 @@ describe("builtin", () => {
 | 
			
		|||
  testEval("1-1", "Ok(0)")
 | 
			
		||||
  testEval("2>1", "Ok(true)")
 | 
			
		||||
  testEval("concat('a','b')", "Ok('ab')")
 | 
			
		||||
  testEval(
 | 
			
		||||
    "addOne(t)=t+1; toInternalSampleArray(mapSamples(fromSamples([1,2,3,4,5,6]), addOne))",
 | 
			
		||||
    "Ok([2,3,4,5,6,7])",
 | 
			
		||||
  )
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
describe("builtin exception", () => {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -37,6 +37,7 @@ module Error = {
 | 
			
		|||
    | LogarithmOfDistributionError(s) => `Logarithm of input error: ${s}`
 | 
			
		||||
    | SampleSetError(TooFewSamples) => "Too Few Samples"
 | 
			
		||||
    | SampleSetError(NonNumericInput(err)) => `Found a non-number in input: ${err}`
 | 
			
		||||
    | SampleSetError(OperationError(err)) => Operation.Error.toString(err)
 | 
			
		||||
    | OperationError(err) => Operation.Error.toString(err)
 | 
			
		||||
    | PointSetConversionError(err) => SampleSetDist.pointsetConversionErrorToString(err)
 | 
			
		||||
    | SparklineError(err) => PointSetTypes.sparklineErrorToString(err)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,12 +1,14 @@
 | 
			
		|||
@genType
 | 
			
		||||
module Error = {
 | 
			
		||||
  @genType
 | 
			
		||||
  type sampleSetError = TooFewSamples | NonNumericInput(string)
 | 
			
		||||
  type sampleSetError =
 | 
			
		||||
    TooFewSamples | NonNumericInput(string) | OperationError(Operation.operationError)
 | 
			
		||||
 | 
			
		||||
  let sampleSetErrorToString = (err: sampleSetError): string =>
 | 
			
		||||
    switch err {
 | 
			
		||||
    | TooFewSamples => "Too few samples when constructing sample set"
 | 
			
		||||
    | NonNumericInput(err) => `Found a non-number in input: ${err}`
 | 
			
		||||
    | OperationError(err) => Operation.Error.toString(err)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
  @genType
 | 
			
		||||
| 
						 | 
				
			
			@ -16,6 +18,8 @@ module Error = {
 | 
			
		|||
    switch err {
 | 
			
		||||
    | TooFewSamplesForConversionToPointSet => "Too Few Samples to convert to point set"
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
  let fromOperationError = e => OperationError(e)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
include Error
 | 
			
		||||
| 
						 | 
				
			
			@ -83,6 +87,14 @@ let sampleN = (t: t, n) => {
 | 
			
		|||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
let samplesMap = (~fn: float => result<float, Operation.Error.t>, t: t): result<
 | 
			
		||||
  t,
 | 
			
		||||
  sampleSetError,
 | 
			
		||||
> => {
 | 
			
		||||
  let samples = T.get(t)->E.A2.fmap(fn)
 | 
			
		||||
  E.A.R.firstErrorOrOpen(samples)->E.R2.errMap(Error.fromOperationError) |> E.R2.bind(make)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//TODO: Figure out what to do if distributions are different lengths. ``zip`` is kind of inelegant for this.
 | 
			
		||||
let map2 = (~fn: (float, float) => result<float, Operation.Error.t>, ~t1: t, ~t2: t): result<
 | 
			
		||||
  t,
 | 
			
		||||
| 
						 | 
				
			
			@ -96,7 +108,7 @@ let map2 = (~fn: (float, float) => result<float, Operation.Error.t>, ~t1: t, ~t2
 | 
			
		|||
  // I could prove this to the type system (say, creating a {first: float, second: float, ..., fifth: float, rest: array<float>}
 | 
			
		||||
  // But doing so would take too much time, so I'll leave it as an assertion
 | 
			
		||||
  E.A.R.firstErrorOrOpen(samples)->E.R2.fmap(x =>
 | 
			
		||||
    E.R.toExn("Input of samples should be larger than 5", make(x))
 | 
			
		||||
    E.R.toExnFnString(Error.sampleSetErrorToString, make(x))
 | 
			
		||||
  )
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -99,6 +99,18 @@ let callInternal = (call: functionCall, environment, reducer: ExpressionT.reduce
 | 
			
		|||
    rMappedList->Result.map(mappedList => mappedList->Belt.List.toArray->EvArray)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  let doMapSampleSetDist = (sampleSetDist: SampleSetDist.t, aLambdaValue) => {
 | 
			
		||||
    let fn = r =>
 | 
			
		||||
      switch Lambda.doLambdaCall(aLambdaValue, list{EvNumber(r)}, environment, reducer) {
 | 
			
		||||
      | Ok(EvNumber(f)) => Ok(f)
 | 
			
		||||
      | _ => Error(Operation.SampleMapNeedsNtoNFunction)
 | 
			
		||||
      }
 | 
			
		||||
    switch SampleSetDist.samplesMap(~fn, sampleSetDist) {
 | 
			
		||||
    | Ok(r) => Ok(EvDistribution(SampleSet(r)))
 | 
			
		||||
    | Error(r) => Error(REDistributionError(SampleSetError(r)))
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  let doReduceArray = (aValueArray, initialValue, aLambdaValue) => {
 | 
			
		||||
    aValueArray->Belt.Array.reduce(Ok(initialValue), (rAcc, elem) =>
 | 
			
		||||
      rAcc->Result.flatMap(acc =>
 | 
			
		||||
| 
						 | 
				
			
			@ -128,6 +140,8 @@ let callInternal = (call: functionCall, environment, reducer: ExpressionT.reduce
 | 
			
		|||
  | ("keep", [EvArray(aValueArray), EvLambda(aLambdaValue)]) =>
 | 
			
		||||
    doKeepArray(aValueArray, aLambdaValue)
 | 
			
		||||
  | ("map", [EvArray(aValueArray), EvLambda(aLambdaValue)]) => doMapArray(aValueArray, aLambdaValue)
 | 
			
		||||
  | ("mapSamples", [EvDistribution(SampleSet(dist)), EvLambda(aLambdaValue)]) =>
 | 
			
		||||
    doMapSampleSetDist(dist, aLambdaValue)
 | 
			
		||||
  | ("reduce", [EvArray(aValueArray), initialValue, EvLambda(aLambdaValue)]) =>
 | 
			
		||||
    doReduceArray(aValueArray, initialValue, aLambdaValue)
 | 
			
		||||
  | ("reduceReverse", [EvArray(aValueArray), initialValue, EvLambda(aLambdaValue)]) =>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -4,6 +4,7 @@ type errorValue =
 | 
			
		|||
  | REArrayIndexNotFound(string, int)
 | 
			
		||||
  | REAssignmentExpected
 | 
			
		||||
  | REDistributionError(DistributionTypes.error)
 | 
			
		||||
  | REOperationError(Operation.operationError)
 | 
			
		||||
  | REExpressionExpected
 | 
			
		||||
  | REFunctionExpected(string)
 | 
			
		||||
  | REJavaScriptExn(option<string>, option<string>) // Javascript Exception
 | 
			
		||||
| 
						 | 
				
			
			@ -29,6 +30,7 @@ let errorToString = err =>
 | 
			
		|||
  | REExpressionExpected => "Expression expected"
 | 
			
		||||
  | REFunctionExpected(msg) => `Function expected: ${msg}`
 | 
			
		||||
  | REDistributionError(err) => `Distribution Math Error: ${DistributionTypes.Error.toString(err)}`
 | 
			
		||||
  | REOperationError(err) => `Math Error: ${Operation.Error.toString(err)}`
 | 
			
		||||
  | REJavaScriptExn(omsg, oname) => {
 | 
			
		||||
      let answer = "JS Exception:"
 | 
			
		||||
      let answer = switch oname {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -306,6 +306,8 @@ let dispatchToGenericOutput = (
 | 
			
		|||
    Helpers.toDistFn(ToSampleSet(Belt.Int.fromFloat(float)), dist, ~env)
 | 
			
		||||
  | ("toSampleSet", [EvDistribution(dist)]) =>
 | 
			
		||||
    Helpers.toDistFn(ToSampleSet(env.sampleCount), dist, ~env)
 | 
			
		||||
  | ("toInternalSampleArray", [EvDistribution(SampleSet(dist))]) =>
 | 
			
		||||
    Some(FloatArray(SampleSetDist.T.get(dist)))
 | 
			
		||||
  | ("fromSamples", [EvArray(inputArray)]) => {
 | 
			
		||||
      let _wrapInputErrors = x => SampleSetDist.NonNumericInput(x)
 | 
			
		||||
      let parsedArray = Helpers.parseNumberArray(inputArray)->E.R2.errMap(_wrapInputErrors)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -235,13 +235,16 @@ module R = {
 | 
			
		|||
    | Ok(a) => f(a)
 | 
			
		||||
    | Error(err) => Error(err)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
  let toExn = (msg: string, x: result<'a, 'b>): 'a =>
 | 
			
		||||
    switch x {
 | 
			
		||||
    | Ok(r) => r
 | 
			
		||||
    | Error(_) => raise(Assertion(msg))
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
  let toExnFnString = (errorToStringFn, o) =>
 | 
			
		||||
    switch o {
 | 
			
		||||
    | Ok(r) => r
 | 
			
		||||
    | Error(r) => raise(Assertion(errorToStringFn(r)))
 | 
			
		||||
    }
 | 
			
		||||
  let default = (default, res: Belt.Result.t<'a, 'b>) =>
 | 
			
		||||
    switch res {
 | 
			
		||||
    | Ok(r) => r
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -55,6 +55,7 @@ type operationError =
 | 
			
		|||
  | ComplexNumberError
 | 
			
		||||
  | InfinityError
 | 
			
		||||
  | NegativeInfinityError
 | 
			
		||||
  | SampleMapNeedsNtoNFunction
 | 
			
		||||
  | PdfInvalidError
 | 
			
		||||
  | NotYetImplemented // should be removed when `klDivergence` for mixed and discrete is implemented.
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -69,6 +70,7 @@ module Error = {
 | 
			
		|||
    | ComplexNumberError => "Operation returned complex result"
 | 
			
		||||
    | InfinityError => "Operation returned positive infinity"
 | 
			
		||||
    | NegativeInfinityError => "Operation returned negative infinity"
 | 
			
		||||
    | SampleMapNeedsNtoNFunction => "SampleMap needs a function that converts a number to a number"
 | 
			
		||||
    | PdfInvalidError => "This Pdf is invalid"
 | 
			
		||||
    | NotYetImplemented => "This pathway is not yet implemented"
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue
	
	Block a user