public static double[] SolveInverse( IForwardSolver forwardSolver, IOptimizer optimizer, SolutionDomainType solutionDomainType, double[] dependentValues, double[] standardDeviationValues, InverseFitType inverseFitType, object[] independentValues, double[] lowerBounds, double[] upperBounds) { //var opticalPropertyGuess = ((OpticalProperties[]) (independentValues[0])).First(); //var fitParameters = new double[4] { opticalPropertyGuess.Mua, opticalPropertyGuess.Musp, opticalPropertyGuess.G, opticalPropertyGuess.N }; var parametersToFit = GetParametersToFit(inverseFitType); var opticalPropertyGuess = (OpticalProperties[])(independentValues[0]); var fitParametersArray = opticalPropertyGuess.SelectMany(opgi => new[] { opgi.Mua, opgi.Musp, opgi.G, opgi.N }).ToArray(); var parametersToFitArray = Enumerable.Range(0, opticalPropertyGuess.Count()).SelectMany(_ => parametersToFit).ToArray(); Func <double[], object[], double[]> func = GetForwardReflectanceFuncForOptimization(forwardSolver, solutionDomainType); var fit = optimizer.SolveWithConstraints(fitParametersArray, parametersToFitArray, lowerBounds, upperBounds, dependentValues.ToArray(), standardDeviationValues.ToArray(), func, independentValues.ToArray()); return(fit); }
public static Complex[] ComputeFluenceComplex( IForwardSolver forwardSolver, FluenceSolutionDomainType solutionDomainType, // keeping us from uniting the above. needs to be a single SolutionDomainType enum IndependentVariableAxis[] independentAxesTypes, double[][] independentValues, OpticalProperties opticalProperties, params double[] constantValues) { var parameters = new double[4] { opticalProperties.Mua, opticalProperties.Musp, opticalProperties.G, opticalProperties.N }; // todo: current assumption below is that the second axes is z. need to generalize var func = GetForwardFluenceFuncComplex(forwardSolver, solutionDomainType, independentAxesTypes[0]); // create a list of inputs (besides optical properties) that corresponds to the behavior of the function above List <object> inputValues = new List <object>(independentValues); constantValues.ForEach(cv => inputValues.Add(cv)); return(func(parameters, inputValues.ToArray())); }
// Steady-State Domain... ///// <summary> ///// Steady-State centerline Photon Hitting Density by the Green's function multiplication ///// </summary> ///// <param name="ops">optical properties object</param> ///// <param name="rhos">Source Detector separation</param> ///// <param name="rProbe">Radial distance from source to "iterogation" location</param> ///// <param name="z">Depth being probed</param> ///// <returns>The Photon Hitting Density at specified location</returns> //public IEnumerable<double> SteadyStatePointSourceCenterlinePHD(IEnumerable<OpticalProperties> ops, IEnumerable<double> rProbes, // IEnumerable<double> rhos, IEnumerable<double> zs) //{ // foreach (var op in ops) // { // DiffusionParameters dp = DiffusionParameters.Create(op, ForwardModel.SDA); // foreach (var rProbe in rProbes) // { // foreach (var rho in rhos) // { // foreach (var z in zs) // { // var r11 = DiffusionBase.Radius1(rProbe, z, dp.zp); // var r12 = DiffusionBase.Radius2(rProbe, z, dp.zp, dp.zb); // var r21 = DiffusionBase.Radius1(rho - rProbe, z, 0.0); // var r22 = DiffusionBase.Radius2(rho - rProbe, z, 0.0, dp.zb); // var fluence1 = DiffusionBase.SteadyStatePointSourceImageGreensFunction(dp, r11, r12); // var fluence2 = DiffusionBase.SteadyStatePointSourceImageGreensFunction(dp, r21, r22); // yield return (fluence1 * fluence2); // } // } // } // } //} public static IEnumerable <Complex> TimeFrequencyDomainFluence2SurfacePointPHD( this IForwardSolver myForwardSolver, double timeModulationFrequency, IEnumerable <OpticalProperties> ops, IEnumerable <double> rhoPrimes, IEnumerable <double> zs) { foreach (var op in ops) { DiffusionParameters dp = DiffusionParameters.Create(op, ForwardModel.SDA); Complex k = ( (op.Mua * dp.cn + Complex.ImaginaryOne * timeModulationFrequency * 2 * Math.PI) / (dp.D * dp.cn) ).SquareRoot(); foreach (var rho in rhoPrimes) { foreach (var z in zs) { var r21 = CalculatorToolbox.GetRadius(rho, z); var r22 = CalculatorToolbox.GetRadius(rho, z + 2 * dp.zb); yield return (DiffusionGreensFunctions.TemporalFrequencyPointSourceImageGreensFunction(dp, r21, r22, k)); } } } }
private static Func <double[], object[], double[]> GetForwardReflectanceFuncForOptimization( IForwardSolver fs, SolutionDomainType type) { Func <object[], double[]> forwardReflectanceFunc = GetForwardReflectanceFunc(fs, type); return((fitData, allParameters) => { // place optical property array in the first position, and the rest following var forwardData = ((object)UnFlattenOpticalProperties(fitData)).AsEnumerable().Concat(allParameters.Skip(1)).ToArray(); return forwardReflectanceFunc(forwardData); }); }
private static Func <object[], double[]> GetForwardReflectanceFunc(IForwardSolver fs, SolutionDomainType type) { switch (type) { case SolutionDomainType.ROfRho: if (fs is TwoLayerSDAForwardSolver) // todo: future generalization to IMultiRegionForwardSolver? { return(forwardData => fs.ROfRho((IOpticalPropertyRegion[][])forwardData[0], (double[])forwardData[1])); } return(forwardData => fs.ROfRho(ops: (OpticalProperties[])forwardData[0], rhos: (double[])forwardData[1])); case SolutionDomainType.ROfFx: if (fs is TwoLayerSDAForwardSolver) { return(forwardData => fs.ROfFx((IOpticalPropertyRegion[][])forwardData[0], (double[])forwardData[1])); } return(forwardData => fs.ROfFx(ops: (OpticalProperties[])forwardData[0], fxs: (double[])forwardData[1])); case SolutionDomainType.ROfRhoAndTime: if (fs is TwoLayerSDAForwardSolver) { return(forwardData => fs.ROfRhoAndTime((IOpticalPropertyRegion[][])forwardData[0], (double[])forwardData[1], (double[])forwardData[2])); } return(forwardData => fs.ROfRhoAndTime(ops: (OpticalProperties[])forwardData[0], rhos: (double[])forwardData[1], ts: (double[])forwardData[2])); case SolutionDomainType.ROfFxAndTime: if (fs is TwoLayerSDAForwardSolver) { return(forwardData => fs.ROfFxAndTime((IOpticalPropertyRegion[][])forwardData[0], (double[])forwardData[1], (double[])forwardData[2])); } return(forwardData => fs.ROfFxAndTime(ops: (OpticalProperties[])forwardData[0], fxs: (double[])forwardData[1], ts: (double[])forwardData[2])); case SolutionDomainType.ROfRhoAndFt: if (fs is TwoLayerSDAForwardSolver) { return(forwardData => fs.ROfRhoAndFt((IOpticalPropertyRegion[][])forwardData[0], (double[])forwardData[1], (double[])forwardData[2]).FlattenRealAndImaginary()); } return(forwardData => fs.ROfRhoAndFt(ops: (OpticalProperties[])forwardData[0], rhos: (double[])forwardData[1], fts: (double[])forwardData[2]).FlattenRealAndImaginary()); case SolutionDomainType.ROfFxAndFt: if (fs is TwoLayerSDAForwardSolver) { return(forwardData => fs.ROfFxAndFt((IOpticalPropertyRegion[][])forwardData[0], (double[])forwardData[1], (double[])forwardData[2]).FlattenRealAndImaginary()); } return(forwardData => fs.ROfFxAndFt(ops: (OpticalProperties[])forwardData[0], fxs: (double[])forwardData[1], fts: (double[])forwardData[2]).FlattenRealAndImaginary()); default: throw new ArgumentOutOfRangeException("type"); } }
// overload for ITissueRegion forward solvers todo: merge with above? public static double[] ComputeFluence( IForwardSolver forwardSolver, FluenceSolutionDomainType solutionDomainType, // keeping us from uniting the above. needs to be a single SolutionDomainType enum IndependentVariableAxis[] independentAxesTypes, double[][] independentValues, IOpticalPropertyRegion[] tissueRegions, params double[] constantValues) { var parameters = tissueRegions.SelectMany(region => { double[] regionParameters = null; if (region is ILayerOpticalPropertyRegion) { var layerRegion = (ILayerOpticalPropertyRegion)region; regionParameters = new[] { layerRegion.RegionOP.Mua, layerRegion.RegionOP.Musp, layerRegion.RegionOP.G, layerRegion.RegionOP.N, layerRegion.ZRange.Delta }; } //else if(region is EllipsoidTissueRegion) //{ // //} else { throw new Exception("Forward model " + forwardSolver.ToString() + " is not supported."); } return(regionParameters); }).ToArray(); // todo: current assumption below is that the second axis is z. need to generalize var func = GetForwardFluenceFunc(forwardSolver, solutionDomainType, independentAxesTypes[0]); // create a list of inputs (besides optical properties) that corresponds to the behavior of the function above List <object> inputValues = new List <object>(independentValues); constantValues.ForEach(cv => inputValues.Add(cv)); return(func(parameters, inputValues.ToArray())); }
private static Func <double[], object[], Complex[]> GetForwardFluenceFuncComplex( IForwardSolver fs, FluenceSolutionDomainType type, IndependentVariableAxis axis) { Func <double[], OpticalProperties> getOP = op => new OpticalProperties(op[0], op[1], op[2], op[3]); // note: the following uses the convention that the independent variable(s) is (are) first in the forward data object array // note: secondly, if there are multiple independent axes, they will be assigned in order of appearance in the method signature switch (type) { case FluenceSolutionDomainType.FluenceOfRhoAndZAndFt: switch (axis) { case IndependentVariableAxis.Rho: if (fs is TwoLayerSDAForwardSolver) // todo: future generalization to IMultiRegionForwardSolver? { return((fitData, otherData) => fs.FluenceOfRhoAndZAndFt(new[] { getLayerTissueRegionArray(fitData) }, (double[])otherData[0], (double[])otherData[1], new[] { (double)otherData[2] })); } return((fitData, otherData) => fs.FluenceOfRhoAndZAndFt(new[] { getOP(fitData) }, (double[])otherData[0], (double[])otherData[1], new[] { (double)otherData[2] })); case IndependentVariableAxis.Ft: if (fs is TwoLayerSDAForwardSolver) { return((fitData, otherData) => fs.FluenceOfRhoAndZAndFt(new[] { getLayerTissueRegionArray(fitData) }, (double[])otherData[2], (double[])otherData[1], new[] { (double)otherData[0] })); } return((fitData, otherData) => fs.FluenceOfRhoAndZAndFt(new[] { getOP(fitData) }, new[] { (double)otherData[2] }, (double[])otherData[1], (double[])otherData[0])); default: throw new ArgumentOutOfRangeException("axis"); } case FluenceSolutionDomainType.FluenceOfFxAndZAndFt: switch (axis) { case IndependentVariableAxis.Fx: return((fitData, otherData) => fs.FluenceOfFxAndZAndFt(new[] { getOP(fitData) }, (double[])otherData[0], (double[])otherData[1], new[] { (double)otherData[2] })); case IndependentVariableAxis.Ft: return((fitData, otherData) => fs.FluenceOfFxAndZAndFt(new[] { getOP(fitData) }, new[] { (double)otherData[2] }, (double[])otherData[1], (double[])otherData[0])); default: throw new ArgumentOutOfRangeException("axis"); } default: throw new ArgumentOutOfRangeException("type"); } }
// overload that calls the above method with just one set of optical properties public static double[] ComputeFluence( IForwardSolver forwardSolver, FluenceSolutionDomainType solutionDomainType, // keeping us from uniting the above. needs to be a single SolutionDomainType enum IndependentVariableAxis[] independentAxesTypes, double[][] independentValues, OpticalProperties opticalProperties, params double[] constantValues) { return(ComputeFluence( forwardSolver, solutionDomainType, independentAxesTypes, independentValues, new[] { opticalProperties }, constantValues)); }
public static double[] ComputeReflectance( IForwardSolver forwardSolver, SolutionDomainType solutionDomainType, ForwardAnalysisType forwardAnalysisType, object[] independentValues) { Func <object[], double[]> func = GetForwardReflectanceFunc(forwardSolver, solutionDomainType); //Func<SolutionDomainType, ForwardAnalysisType, IndependentVariableAxis[], double[]> getOptimizationParameters = (_,_,_) => // new[] { op.Mua, op.Musp, op.G, op.N } //double[] optimizationParameters = GetOptimizationParameters(forwardSolver, solutionDomainType, independentAxisTypes); // will need this for inverse solver // create a list of inputs (besides optical properties) that corresponds to the behavior of the function above return(forwardAnalysisType == ForwardAnalysisType.R ? func(independentValues) : func.GetDerivativeFunc(forwardAnalysisType)(independentValues)); }
/// <summary> /// Method to generate PHD /// </summary> /// <param name="forwardSolver">forward solver</param> /// <param name="fluence">fluence</param> /// <param name="sdSeparation">source detector separation (in mm)</param> /// <param name="ops">optical properties</param> /// <param name="rhos">detector locations (in mm)</param> /// <param name="zs">z values (in mm)</param> /// <returns></returns> public static double[] GetPHD(IForwardSolver forwardSolver, double[] fluence, double sdSeparation, OpticalProperties[] ops, double[] rhos, double[] zs) { var rhoPrimes = from r in rhos select r - sdSeparation; var greensFunction = forwardSolver.SteadyStateFluence2SurfacePointPHD(ops, rhoPrimes, zs); var phd = new double[fluence.Length]; phd.PopulateFromEnumerable(Enumerable.Zip(fluence, greensFunction, (flu, green) => flu * green)); return(phd); // replaced with pre-initialized array + "PopulateFromEnumerable" call instead of growing array dynamically //System.Linq.Enumerable.Zip(fluence, greensFunction, (flu, green) => flu * green).ToArray(); }
public static IEnumerable <double> SteadyStateFluence2SurfacePointPHD( this IForwardSolver myForwardSolver, IEnumerable <OpticalProperties> ops, IEnumerable <double> rhoPrimes, IEnumerable <double> zs) { foreach (var op in ops) { DiffusionParameters dp = DiffusionParameters.Create(op, ForwardModel.SDA); foreach (var rho in rhoPrimes) { foreach (var z in zs) { var r21 = CalculatorToolbox.GetRadius(rho, z); var r22 = CalculatorToolbox.GetRadius(rho, z + 2 * dp.zb); yield return (DiffusionGreensFunctions.StationaryPointSourceImageGreensFunction(dp, r21, r22)); } } } }
public static double[] ComputeFluence( IForwardSolver forwardSolver, FluenceSolutionDomainType solutionDomainType, // keeping us from uniting the above. needs to be a single SolutionDomainType enum IndependentVariableAxis[] independentAxesTypes, double[][] independentValues, OpticalProperties[] opticalProperties, params double[] constantValues) { // todo: current assumption below is that the second axes is z. need to generalize var func = GetForwardFluenceFunc(forwardSolver, solutionDomainType, independentAxesTypes[0]); // create a list of inputs (besides optical properties) that corresponds to the behavior of the function above List <object> inputValues = new List <object>(independentValues); constantValues.ForEach(cv => inputValues.Add(cv)); if (opticalProperties.Length == 1) // optimization that skips duplicate arrays if we're not multiplexing over optical properties (e.g. wavelength) { var op = opticalProperties[0]; var parameters = new[] { op.Mua, op.Musp, op.G, op.N }; return(func(parameters, inputValues.ToArray())); } var numOp = opticalProperties.Length; var numIv = independentValues.Length; var fluence = new double[numOp * numIv]; for (int opi = 0; opi < numOp; opi++) // todo: parallelize { var op = opticalProperties[opi]; var parameters = new[] { op.Mua, op.Musp, op.G, op.N }; var tempValues = func(parameters, inputValues.ToArray()); for (int ivi = 0; ivi < numIv; ivi++) { fluence[opi * numIv + ivi] = tempValues[ivi]; } } return(fluence); }
public static IEnumerable <double> TemporalPointSourceCenterlinePHD( this IForwardSolver myForwardSolver, IEnumerable <OpticalProperties> ops, IEnumerable <double> rProbes, IEnumerable <double> rhos, IEnumerable <double> zs, IEnumerable <double> ts) { foreach (var op in ops) { DiffusionParameters dp = DiffusionParameters.Create(op, ForwardModel.SDA); foreach (var rProbe in rProbes) { foreach (var rho in rhos) { foreach (var z in zs) { var r11 = CalculatorToolbox.GetRadius(rProbe, z - dp.zp); var r12 = CalculatorToolbox.GetRadius(rProbe, z + dp.zp + 2 * dp.zb); var r21 = CalculatorToolbox.GetRadius(rho - rProbe, z); var r22 = CalculatorToolbox.GetRadius(rho - rProbe, z + 2 * dp.zb); foreach (var t in ts) { Func <double, double> integrandConvolve = tau => { return (DiffusionGreensFunctions.TemporalPointSourceImageGreensFunction (dp, r11, r12, tau) * DiffusionGreensFunctions.TemporalPointSourceImageGreensFunction (dp, r21, r22, t - tau)); }; yield return(FunctionMath.Integrate( integrandConvolve, Meta.Numerics.Interval.FromEndpoints(0.0, t))); } } } } } }
///// <summary> ///// Modified from a code of Fred's written in Matlab ///// </summary> ///// <param name="dp">diffusion parameters object</param> ///// <param name="k">complex diffusion constant, ((mua*cn +i*(ft*2*pi))/(D*cn)).^0.5</param> ///// <param name="rho">source-detector separation</param> ///// <param name="rProbe">radius from source</param> ///// <param name="y">omitted</param> ///// <param name="z">depth</param> ///// <param name="ft">temporal frequency</param> ///// <returns></returns> //public static double DepthProbFrequencyDomainPhotonMigration(DiffusionParameters dp, Complex k, // double rho, double rProbe, double z, double ft) //{ // var r11 = DiffusionBase.Radius1(rho, z, dp.zp); // var r12 = DiffusionBase.Radius2(rho, z, dp.zp, dp.zb); // var r21 = DiffusionBase.Radius1(rProbe - rho, z, 0.0); // var r22 = DiffusionBase.Radius2(rProbe - rho, z, 0.0, dp.zb); // var phi1 = SDAForwardSolver.TemporalFrequencyFluence(dp, k, r11, r12); // var phi2 = SDAForwardSolver.TemporalFrequencyFluence(dp, k, r21, r22); // return (phi1 * phi2).Modulus; // see Kienle and Patterson, JOSA A 14(1), 246-254,1997 //} public static IEnumerable <double> TemporalFrequencyPointSourceCenterlinePHD( this IForwardSolver myForwardSolver, IEnumerable <OpticalProperties> ops, IEnumerable <double> rProbes, IEnumerable <double> rhos, IEnumerable <double> zs, IEnumerable <double> fts) { foreach (var op in ops) { DiffusionParameters dp = DiffusionParameters.Create(op, ForwardModel.SDA); foreach (var rProbe in rProbes) { foreach (var rho in rhos) { foreach (var z in zs) { var r11 = CalculatorToolbox.GetRadius(rProbe, z - dp.zp); var r12 = CalculatorToolbox.GetRadius(rProbe, z + dp.zp + 2 * dp.zb); var r21 = CalculatorToolbox.GetRadius(rho - rProbe, z); var r22 = CalculatorToolbox.GetRadius(rho - rProbe, z + 2 * dp.zb); foreach (var ft in fts) { Complex k = ( (op.Mua * dp.cn + Complex.ImaginaryOne * ft * 2 * Math.PI) / (dp.D * dp.cn) ).SquareRoot(); var phi1 = DiffusionGreensFunctions.TemporalFrequencyPointSourceImageGreensFunction( dp, r11, r12, k); var phi2 = DiffusionGreensFunctions.TemporalFrequencyPointSourceImageGreensFunction( dp, r21, r22, k); yield return((phi1 * phi2).Magnitude); } } } } } }
// todo: array overloads for fluence forward solvers too private static Func <double[], object[], double[]> GetForwardFluenceFunc( IForwardSolver fs, FluenceSolutionDomainType type, IndependentVariableAxis axis) { Func <double[], OpticalProperties> getOP = op => new OpticalProperties(op[0], op[1], op[2], op[3]); // note: the following uses the convention that the independent variable(s) is (are) first in the forward data object array // note: secondly, if there are multiple independent axes, they will be assigned in order of appearance in the method signature switch (type) { case FluenceSolutionDomainType.FluenceOfRhoAndZ: if (fs is TwoLayerSDAForwardSolver) // todo: future generalization to IMultiRegionForwardSolver? { return((fitData, otherData) => fs.FluenceOfRhoAndZ(new[] { getLayerTissueRegionArray(fitData) }, (double[])otherData[0], (double[])otherData[1])); } return((fitData, otherData) => fs.FluenceOfRhoAndZ(new[] { getOP(fitData) }, (double[])otherData[0], (double[])otherData[1])); case FluenceSolutionDomainType.FluenceOfFxAndZ: return((fitData, otherData) => fs.FluenceOfFxAndZ(new[] { getOP(fitData) }, (double[])otherData[0], (double[])otherData[1])); case FluenceSolutionDomainType.FluenceOfRhoAndZAndTime: switch (axis) { case IndependentVariableAxis.Rho: return((fitData, otherData) => fs.FluenceOfRhoAndZAndTime(new[] { getOP(fitData) }, (double[])otherData[0], (double[])otherData[1], new[] { (double)otherData[2] })); case IndependentVariableAxis.Time: return((fitData, otherData) => fs.FluenceOfRhoAndZAndTime(new[] { getOP(fitData) }, new[] { (double)otherData[2] }, (double[])otherData[1], (double[])otherData[0])); //case IndependentVariableAxis.Wavelength: // return (chromPlusMusp, constantData) => // { // var wv = (double[]) constantData[0]; // var tissue = (Tissue) constantData[1]; // int i = 0; // tissue.Absorbers.ForEach(abs => abs.Concentration = chromPlusMusp[i++]); // tissue.Scatterer = new PowerLawScatterer(chromPlusMusp[i], chromPlusMusp[i + 1]); // var muas = wv.Select(w => tissue.GetMua(w)); // var musps = wv.Select(w => tissue.GetMusp(w)); // return EnumerableExtensions.Zip(muas,musps,(mua,musp)=>fs.ROfRhoAndTime())... // }; // return op => fs.ROfRhoAndTime(op, ((double)constantValues[0]).AsEnumerable(), ((double)constantValues[1]).AsEnumerable()); default: throw new ArgumentOutOfRangeException("axis"); } case FluenceSolutionDomainType.FluenceOfFxAndZAndTime: switch (axis) { case IndependentVariableAxis.Fx: return((fitData, otherData) => fs.FluenceOfFxAndZAndTime(new[] { getOP(fitData) }, (double[])otherData[0], (double[])otherData[1], new[] { (double)otherData[2] })); case IndependentVariableAxis.Time: return((fitData, otherData) => fs.FluenceOfFxAndZAndTime(new[] { getOP(fitData) }, new[] { (double)otherData[2] }, (double[])otherData[1], (double[])otherData[0])); default: throw new ArgumentOutOfRangeException("axis"); } default: throw new ArgumentOutOfRangeException("type"); } }
public VectorizedForwardSolverFuncs(IForwardSolver fs) { _fs = fs; }