{"id":2382,"date":"2025-03-12T20:07:06","date_gmt":"2025-03-12T20:07:06","guid":{"rendered":"https:\/\/garikoitz.info\/blog\/?p=2382"},"modified":"2025-03-12T21:52:29","modified_gmt":"2025-03-12T21:52:29","slug":"regresion-no-lineal-accord-net-vs-math-net-numerics","status":"publish","type":"post","link":"https:\/\/garikoitz.info\/blog\/2025\/03\/regresion-no-lineal-accord-net-vs-math-net-numerics\/","title":{"rendered":"Regresi\u00f3n no lineal &#8211; Accord.NET vs Math.NET Numerics"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">Recientemente he estado trabajando bastante con la librer\u00eda <strong>Math.NET Numerics<\/strong>, y debo decir que me ha dejado muy buen sabor de boca. Es por ello que he decidido volver a plantear el problema de regresi\u00f3n no lineal, originalmente abordado en la entrada \u201c<a href=\"https:\/\/garikoitz.info\/blog\/2021\/06\/regresion-lineal-y-no-lineal-excel-vs-net\/\" target=\"_blank\" rel=\"noreferrer noopener\">Regresi\u00f3n lineal y no lineal &#8211; Excel vs NET<\/a>\u201d, para realizar una comparativa entre la ya conocida <strong>Accord <\/strong>y la reciente incorporaci\u00f3n de Math.NET Numerics.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">En esta ocasi\u00f3n ir\u00e9 directo al grano y os presentar\u00e9 ambas implementaciones en VB.Net. Como recordatorio, el problema consiste en obtener, mediante regresi\u00f3n no lineal, los tres par\u00e1metros de la ecuaci\u00f3n de Antoine a partir de unas temperaturas y presiones dadas.<\/p>\n\n\n\n<div id=\"ez-toc-container\" class=\"ez-toc-v2_0_82_2 counter-hierarchy ez-toc-counter ez-toc-grey ez-toc-container-direction\">\n<div class=\"ez-toc-title-container\">\n<p class=\"ez-toc-title\" style=\"cursor:inherit\">\u00cdndice<\/p>\n<span class=\"ez-toc-title-toggle\"><a href=\"#\" class=\"ez-toc-pull-right ez-toc-btn ez-toc-btn-xs ez-toc-btn-default ez-toc-toggle\" aria-label=\"Alternar tabla de contenidos\"><span class=\"ez-toc-js-icon-con\"><span class=\"\"><span class=\"eztoc-hide\" style=\"display:none;\">Toggle<\/span><span class=\"ez-toc-icon-toggle-span\"><svg style=\"fill: #999;color:#999\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\" class=\"list-377408\" width=\"20px\" height=\"20px\" viewBox=\"0 0 24 24\" fill=\"none\"><path d=\"M6 6H4v2h2V6zm14 0H8v2h12V6zM4 11h2v2H4v-2zm16 0H8v2h12v-2zM4 16h2v2H4v-2zm16 0H8v2h12v-2z\" fill=\"currentColor\"><\/path><\/svg><svg style=\"fill: #999;color:#999\" class=\"arrow-unsorted-368013\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"10px\" height=\"10px\" viewBox=\"0 0 24 24\" version=\"1.2\" baseProfile=\"tiny\"><path d=\"M18.2 9.3l-6.2-6.3-6.2 6.3c-.2.2-.3.4-.3.7s.1.5.3.7c.2.2.4.3.7.3h11c.3 0 .5-.1.7-.3.2-.2.3-.5.3-.7s-.1-.5-.3-.7zM5.8 14.7l6.2 6.3 6.2-6.3c.2-.2.3-.5.3-.7s-.1-.5-.3-.7c-.2-.2-.4-.3-.7-.3h-11c-.3 0-.5.1-.7.3-.2.2-.3.5-.3.7s.1.5.3.7z\"\/><\/svg><\/span><\/span><\/span><\/a><\/span><\/div>\n<nav><ul class='ez-toc-list ez-toc-list-level-1 eztoc-toggle-hide-by-default' ><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-1\" href=\"https:\/\/garikoitz.info\/blog\/2025\/03\/regresion-no-lineal-accord-net-vs-math-net-numerics\/#AccordNET\" >Accord.NET<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-2\" href=\"https:\/\/garikoitz.info\/blog\/2025\/03\/regresion-no-lineal-accord-net-vs-math-net-numerics\/#MathNET_Numerics\" >Math.NET Numerics<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-3\" href=\"https:\/\/garikoitz.info\/blog\/2025\/03\/regresion-no-lineal-accord-net-vs-math-net-numerics\/#Comparativa\" >Comparativa<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-4\" href=\"https:\/\/garikoitz.info\/blog\/2025\/03\/regresion-no-lineal-accord-net-vs-math-net-numerics\/#Conclusiones\" >Conclusiones<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-5\" href=\"https:\/\/garikoitz.info\/blog\/2025\/03\/regresion-no-lineal-accord-net-vs-math-net-numerics\/#Enlaces\" >Enlaces<\/a><\/li><\/ul><\/nav><\/div>\n<h2 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"AccordNET\"><\/span>Accord.NET<span class=\"ez-toc-section-end\"><\/span><\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Los m\u00e1s observadores ya os habr\u00e9is dado cuenta de que he actualizado la funci\u00f3n. La vez anterior no me quede del todo satisfecho con el m\u00ednimo local que devolv\u00eda el algoritmo y he retocado la parte de las iteraciones y la tolerancia para mejorar esto. El resultado es que conseguimos los mismos valores que con la regresi\u00f3n lineal del art\u00edculo anterior, es decir, en esta ocasi\u00f3n el resultado roza la perfecci\u00f3n incluso aunque no le indiquemos valores iniciales.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><a href=\"https:\/\/garikoitz.info\/blog\/wp-content\/uploads\/2025\/03\/image.png\"><img decoding=\"async\" src=\"https:\/\/garikoitz.info\/blog\/wp-content\/uploads\/2025\/03\/image-1024x480.webp\" alt=\"\" class=\"wp-image-2388\"\/><\/a><\/figure>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"visualbasic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\tPublic Sub CalcNoLineal_Accord()\n\t\tTry\n\t\t\t' --- 1. Preparar DataTable y DataGridView (DGV_API_NL) ---\n\t\t\tTxt_Resul_NL.Text = \"\"\n\t\t\tTXT_A.Text = \"\"\n\t\t\tTXT_B.Text = \"\"\n\t\t\tTXT_C.Text = \"\"\n\n\t\t\tDim dt As Date = Now\n\t\t\tTxt_Resul_NL.Text = \"C\u00e1lculos - \" &amp; dt.ToString(\"dd\/MM\/yyyy HH:mm:ss\", Globalization.CultureInfo.InvariantCulture) &amp; vbNewLine &amp; vbNewLine\n\t\t\tTxt_Resul_NL.Text &amp;= \"Regresi\u00f3n No Lineal usando Levenberg-Marquardt (Accord.NET)\" &amp; vbNewLine &amp; vbNewLine\n\n\t\t\tDim dt_api_NL As New DataTable\n\t\t\tdt_api_NL.Columns.Add(\"Temp \u00baF\")\n\t\t\tdt_api_NL.Columns.Add(\"P psia\")\n\t\t\tdt_api_NL.Columns.Add(\"P(calc) psia\")\n\t\t\tdt_api_NL.Columns.Add(\"DP psia\")\n\t\t\tdt_api_NL.Columns.Add(\"Error TCP\")\n\n\t\t\t' Llenar la tabla con 15 datos (\u00edndices 0 a 14)\n\t\t\tFor i As Integer = 0 To 14\n\t\t\t\tdt_api_NL.Rows.Add(New String() {T(i).ToString(), P(i).ToString(), \"0\", \"0\", \"0\"})\n\t\t\tNext\n\n\t\t\tDGV_API_NL.DataSource = dt_api_NL\n\t\t\tDGV_API_NL.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.AllCells\n\t\t\tDGV_API_NL.AllowUserToOrderColumns = False\n\t\t\tDGV_API_NL.RowHeadersVisible = False\n\t\t\tDGV_API_NL.ColumnHeadersVisible = True\n\t\t\tApplication.DoEvents()\n\n\t\t\t' --- 2. Extraer datos de la tabla en arrays ---\n\t\t\tDim rowCount As Integer = dt_api_NL.Rows.Count\n\t\t\tDim TData(rowCount - 1) As Double\n\t\t\tDim PData(rowCount - 1) As Double\n\n\t\t\tFor i As Integer = 0 To rowCount - 1\n\t\t\t\tTData(i) = CDbl(dt_api_NL.Rows(i)(\"Temp \u00baF\"))\n\t\t\t\tPData(i) = CDbl(dt_api_NL.Rows(i)(\"P psia\"))\n\t\t\tNext\n\n\t\t\t' --- 3. Transformar los datos: cada entrada es un vector unidimensional ---\n\t\t\tDim inputs As Double()() = TData.Select(Function(t) New Double() {t}).ToArray()\n\t\t\tDim output As Double() = PData\n\n\t\t\t' --- 4. Configurar la regresi\u00f3n no lineal ---\n\t\t\t' Modelo: P = 10^( A - B\/(T + C) )\n\t\t\tDim nls As New NonlinearLeastSquares()\n\t\t\tWith nls\n\t\t\t\t.NumberOfParameters = 3   ' A, B, C\n\t\t\t\t' Valores iniciales (le\u00eddos de controles)\n\t\t\t\t.StartValues = {CDbl(Txt_A_ini.Text), CDbl(Txt_B_ini.Text), CDbl(Txt_C_ini.Text)}\n\t\t\t\t'\n\t\t\t\t' Funci\u00f3n del modelo: recibe los par\u00e1metros y un vector de entrada (T)\n\t\t\t\t.Function = Function(par As Double(), inp As Double()) As Double\n\t\t\t\t\t\t\t\tReturn 10 ^ (par(0) - par(1) \/ (inp(0) + par(2)))\n\t\t\t\t\t\t\tEnd Function\n\t\t\t\t'\n\t\t\t\t' Funci\u00f3n del gradiente: se debe llenar el arreglo result con las derivadas parciales\n\t\t\t\t.Gradient = Sub(par As Double(), inp As Double(), result() As Double)\n\t\t\t\t\t\t\t\tDim ln10 As Double = Math.Log(10)\n\t\t\t\t\t\t\t\tDim pred As Double = 10 ^ (par(0) - par(1) \/ (inp(0) + par(2)))\n\t\t\t\t\t\t\t\tresult(0) = ln10 * pred                                     ' d\/dA\n\t\t\t\t\t\t\t\tresult(1) = -ln10 * pred \/ (inp(0) + par(2))                 ' d\/dB\n\t\t\t\t\t\t\t\tresult(2) = (par(1) * ln10 * pred) \/ ((inp(0) + par(2)) ^ 2)   ' d\/dC\n\t\t\t\t\t\t\tEnd Sub\n\t\t\tEnd With\n\n\t\t\t' --- 5. Configurar el algoritmo de optimizaci\u00f3n (Levenberg-Marquardt) ---\n\t\t\tDim algorithm As New LevenbergMarquardt()\n\t\t\tWith algorithm\n\t\t\t\t.MaxIterations = 1    ' Iteraciones por bloque\n\t\t\t\t.Tolerance = 0.000000000000001 ' Tolerancia para cambio en SSE -> 1e-15\n\t\t\tEnd With\n\n\t\t\tnls.Algorithm = algorithm\n\n\t\t\t' --- 6. Ejecutar el ajuste con detenci\u00f3n temprana ---\n\t\t\tDim totalMaxIter As Integer = CInt(Txt_iter.Text)\n\t\t\tDim totalIter As Integer = 0\n\t\t\tDim tolerance As Double = 0.000000000000001  '1e-15\n\n\t\t\t' Funci\u00f3n local para calcular el SSE (suma de cuadrados de errores)\n\t\t\tDim ComputeSSE As Func(Of Double(), Double) =\n\t\t\tFunction(coeffs As Double()) As Double\n\t\t\t\tDim sumSq As Double = 0\n\t\t\t\tFor j As Integer = 0 To inputs.Length - 1\n\t\t\t\t\tDim pred As Double = 10 ^ (coeffs(0) - coeffs(1) \/ (inputs(j)(0) + coeffs(2)))\n\t\t\t\t\tDim resid As Double = pred - output(j)\n\t\t\t\t\tsumSq += resid * resid\n\t\t\t\tNext\n\t\t\t\tReturn sumSq\n\t\t\tEnd Function\n\n\t\t\tDim currentSolution() As Double = nls.StartValues\n\t\t\tDim previousCost As Double = ComputeSSE(currentSolution)\n\t\t\tDim currentCost As Double = previousCost\n\n\t\t\tDo\n\t\t\t\tnls.StartValues = currentSolution\n\t\t\t\tDim regression As NonlinearRegression = nls.Learn(inputs, output)\n\t\t\t\tcurrentSolution = regression.Coefficients\n\t\t\t\ttotalIter += 1\n\t\t\t\tcurrentCost = ComputeSSE(currentSolution)\n\t\t\t\tIf Math.Abs(previousCost - currentCost) &lt; tolerance Then Exit Do\n\t\t\t\tpreviousCost = currentCost\n\t\t\tLoop While totalIter &lt; totalMaxIter\n\n\t\t\t' --- 7. Mostrar los par\u00e1metros ajustados ---\n\t\t\tTXT_A.Text = currentSolution(0).ToString(\"G5\")\n\t\t\tTXT_B.Text = currentSolution(1).ToString(\"G5\")\n\t\t\tTXT_C.Text = currentSolution(2).ToString(\"G5\")\n\n\t\t\tA = currentSolution(0)\n\t\t\tB = currentSolution(1)\n\t\t\tC = currentSolution(2)\n\t\t\tAPIcalc()\n\n\t\t\t' --- 8. Graficar los resultados ---\n\t\t\tChart2.Series.Clear()\n\t\t\tChart2.Legends.Clear()\n\t\t\tChart2.Legends.Add(\"\")\n\t\t\tChart2.Titles.Clear()\n\t\t\tChart2.Titles.Add(\"\")\n\t\t\tChart2.Titles(0).Font = New Font(\"Microsoft Sans Serif\", 12, FontStyle.Bold)\n\t\t\tFor i As Integer = 1 To 2\n\t\t\t\tChart2.Series.Add(dt_api_NL.Columns(i).ColumnName)\n\t\t\t\tWith Chart2.Series(Chart2.Series.Count - 1)\n\t\t\t\t\t.MarkerStyle = DataVisualization.Charting.MarkerStyle.Square\n\t\t\t\t\t.IsVisibleInLegend = True\n\t\t\t\t\t.ChartType = DataVisualization.Charting.SeriesChartType.Line\n\t\t\t\t\t.BorderWidth = 2\n\t\t\t\t\t.ToolTip = \"#SERIESNAME | T: #VALX{f2} P: #VALY{f2}\"\n\t\t\t\t\tFor j As Integer = 0 To rowCount - 1\n\t\t\t\t\t\tDim Tval As Double = CDbl(dt_api_NL.Rows(j)(\"Temp \u00baF\"))\n\t\t\t\t\t\tDim Pval As Double = CDbl(dt_api_NL.Rows(j)(i))\n\t\t\t\t\t\t.Points.AddXY(Tval, Pval)\n\t\t\t\t\tNext\n\t\t\t\tEnd With\n\t\t\tNext\n\t\t\tChart2.ChartAreas(0).AxisX.Title = \"Temperatura (\u00baF)\"\n\t\t\tChart2.ChartAreas(0).AxisY.Title = \"Presi\u00f3n de vapor (psia)\"\n\t\t\tChart2.ChartAreas(0).RecalculateAxesScale()\n\n\t\t\tTxt_Resul_NL.Text &amp;= \"Total iteraciones: \" &amp; totalIter.ToString() &amp; vbNewLine\n\t\t\tTxt_Resul_NL.Text &amp;= \"Costo final (SSE): \" &amp; currentCost.ToString(\"G5\") &amp; vbNewLine\n\n\t\tCatch ex As Exception\n\t\t\tMsgBox(ex.ToString())\n\t\tEnd Try\n\tEnd Sub<\/pre>\n\n\n\n<h2 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"MathNET_Numerics\"><\/span>Math.NET Numerics<span class=\"ez-toc-section-end\"><\/span><\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Esta implementaci\u00f3n es completamente nueva pero para que os hag\u00e1is una idea, me ha costado m\u00e1s modificar la funci\u00f3n de Accord para que mostrara bien las iteraciones que crear esta funci\u00f3n de cero. Creo que sin duda, eso dice mucho de la facilidad de uso de esta librer\u00eda.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><a href=\"https:\/\/garikoitz.info\/blog\/wp-content\/uploads\/2025\/03\/image-1.png\"><img decoding=\"async\" src=\"https:\/\/garikoitz.info\/blog\/wp-content\/uploads\/2025\/03\/image-1-1024x526.webp\" alt=\"\" class=\"wp-image-2393\"\/><\/a><\/figure>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"visualbasic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">Public Sub CalcNoLineal_MathNet()\n\t\tTry\n\t\t\t' ---------------------------------------------------\n\t\t\t' 1. PREPARAR DataTable Y DataGridView (DGV_API_NL)\n\t\t\t' ---------------------------------------------------\n\t\t\tTxt_Resul_NL.Text = \"\"\n\t\t\tTXT_A.Text = \"\"\n\t\t\tTXT_B.Text = \"\"\n\t\t\tTXT_C.Text = \"\"\n\n\t\t\tDim dt As Date = Now\n\t\t\tTxt_Resul_NL.Text = \"C\u00e1lculos - \" &amp; dt.ToString(\"dd\/MM\/yyyy HH:mm:ss\", CultureInfo.InvariantCulture) &amp; vbNewLine &amp; vbNewLine\n\t\t\tTxt_Resul_NL.Text &amp;= \"Regresi\u00f3n No Lineal usando Levenberg-Marquardt (Math.NET)\" &amp; vbNewLine &amp; vbNewLine\n\n\t\t\t' Creamos la tabla para mostrar Temp, P, etc.\n\t\t\tDim dt_api_NL As New DataTable\n\t\t\tdt_api_NL.Columns.Add(\"Temp \u00baF\")\n\t\t\tdt_api_NL.Columns.Add(\"P psia\")\n\t\t\tdt_api_NL.Columns.Add(\"P(calc) psia\")\n\t\t\tdt_api_NL.Columns.Add(\"DP psia\")\n\t\t\tdt_api_NL.Columns.Add(\"Error TCP\")\n\n\t\t\t' Llenamos la tabla con los 15 datos T(i), P(i)\n\t\t\tFor i = 0 To 14\n\t\t\t\tdt_api_NL.Rows.Add(New String() {\n\t\t\t\tT(i).ToString(),\n\t\t\t\tP(i).ToString(),\n\t\t\t\t\"0\", \"0\", \"0\"\n\t\t\t})\n\t\t\tNext i\n\n\t\t\t' Asignamos al DGV\n\t\t\tDGV_API_NL.DataSource = dt_api_NL\n\t\t\tDGV_API_NL.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.AllCells\n\t\t\tDGV_API_NL.AllowUserToOrderColumns = False\n\t\t\tDGV_API_NL.RowHeadersVisible = False\n\t\t\tDGV_API_NL.ColumnHeadersVisible = True\n\t\t\tApplication.DoEvents()\n\n\t\t\t' ---------------------------------------------------\n\t\t\t' 2. EXTRAER DATOS DE LA TABLA => Vectores TDataVector, PDataVector\n\t\t\t' ---------------------------------------------------\n\t\t\tDim rowCount As Integer = dt_api_NL.Rows.Count\n\t\t\tDim TData As Double() = New Double(rowCount - 1) {}\n\t\t\tDim PData As Double() = New Double(rowCount - 1) {}\n\n\t\t\tFor i = 0 To rowCount - 1\n\t\t\t\tTData(i) = CDbl(dt_api_NL.Rows(i)(\"Temp \u00baF\"))\n\t\t\t\tPData(i) = CDbl(dt_api_NL.Rows(i)(\"P psia\"))\n\t\t\tNext\n\n\t\t\t' Creamos vectores Math.NET\n\t\t\tDim TDataVector As Vector(Of Double) = Vector(Of Double).Build.DenseOfArray(TData)\n\t\t\tDim PDataVector As Vector(Of Double) = Vector(Of Double).Build.DenseOfArray(PData)\n\n\t\t\t' ---------------------------------------------------\n\t\t\t' 3. DEFINIR FUNCION MODELO + JACOBIANO\n\t\t\t'\n\t\t\t' Modelo: P = 10^( A - B\/(T + C) )\n\t\t\t' => firma: Func(Of Vector(Of Double), Double, Double)\n\t\t\t' => jacobiano: Func(Of Vector(Of Double), Double, Vector(Of Double))\n\t\t\t' ---------------------------------------------------\n\t\t\tDim modelFunction As Func(Of Vector(Of Double), Double, Double) =\n\t\t\tFunction(par, x)\n\t\t\t\tDim A_ As Double = par(0)\n\t\t\t\tDim B_ As Double = par(1)\n\t\t\t\tDim C_ As Double = par(2)\n\t\t\t\tDim Q = A_ - (B_ \/ (x + C_))\n\t\t\t\tReturn 10.0 ^ Q\n\t\t\tEnd Function\n\n\t\t\tDim jacobianFunction As Func(Of Vector(Of Double), Double, Vector(Of Double)) =\n\t\t\tFunction(par, x)\n\t\t\t\tDim A_ As Double = par(0)\n\t\t\t\tDim B_ As Double = par(1)\n\t\t\t\tDim C_ As Double = par(2)\n\t\t\t\tDim ln10 As Double = Math.Log(10.0)\n\t\t\t\tDim Q = A_ - (B_ \/ (x + C_))\n\t\t\t\tDim F = 10.0 ^ Q\n\n\t\t\t\tDim dFdA = ln10 * F\n\t\t\t\tDim dFdB = -ln10 * F \/ (x + C_)\n\t\t\t\tDim dFdC = ln10 * F * B_ \/ ((x + C_) ^ 2)\n\n\t\t\t\tReturn Vector(Of Double).Build.DenseOfArray({dFdA, dFdB, dFdC})\n\t\t\tEnd Function\n\n\t\t\t' ---------------------------------------------------\n\t\t\t' 4. CREAR IObjectiveModel CON NonlinearModel\n\t\t\t' ---------------------------------------------------\n\t\t\tDim objective As IObjectiveModel =\n\t\t\tObjectiveFunction.NonlinearModel(\n\t\t\t\tmodelFunction,\n\t\t\t\tjacobianFunction,\n\t\t\t\tTDataVector,\n\t\t\t\tPDataVector\n\t\t\t)\n\n\t\t\t' ---------------------------------------------------\n\t\t\t' 5. LEER ESTIMACION INICIAL Y AJUSTAR\n\t\t\t' ---------------------------------------------------\n\t\t\tDim A_ini As Double = CDbl(Txt_A_ini.Text)\n\t\t\tDim B_ini As Double = CDbl(Txt_B_ini.Text)\n\t\t\tDim C_ini As Double = CDbl(Txt_C_ini.Text)\n\t\t\tDim initialGuess = Vector(Of Double).Build.DenseOfArray(New Double() {A_ini, B_ini, C_ini})\n\n\t\t\tDim lm As New LevenbergMarquardtMinimizer()\n\t\t\tlm.MaximumIterations = CInt(Txt_iter.Text)\n\n\t\t\tDim result = lm.FindMinimum(objective, initialGuess)\n\n\t\t\t' ---------------------------------------------------\n\t\t\t' 6. Extraer par\u00e1metros ajustados => {A_Opt, B_Opt, C_Opt}\n\t\t\t' ---------------------------------------------------\n\t\t\tDim pOptim = result.MinimizingPoint\n\t\t\tDim A_Opt = pOptim(0)\n\t\t\tDim B_Opt = pOptim(1)\n\t\t\tDim C_Opt = pOptim(2)\n\n\t\t\t' Actualizar TextBoxes\n\t\t\tTXT_A.Text = A_Opt.ToString()\n\t\t\tTXT_B.Text = B_Opt.ToString()\n\t\t\tTXT_C.Text = C_Opt.ToString()\n\n\t\t\t' Mostrar info de convergencia en Txt_Resul_NL\n\t\t\tTxt_Resul_NL.Text &amp;= $\"Estado de convergencia: {result.ReasonForExit}\" &amp; vbNewLine\n\t\t\tTxt_Resul_NL.Text &amp;= $\"Iteraciones: {result.Iterations}\" &amp; vbNewLine &amp; vbNewLine\n\t\t\tTxt_Resul_NL.Text &amp;= $\"Par\u00e1metros encontrados:\" &amp; vbNewLine\n\t\t\tTxt_Resul_NL.Text &amp;= $\"A = {A_Opt}\" &amp; vbNewLine\n\t\t\tTxt_Resul_NL.Text &amp;= $\"B = {B_Opt}\" &amp; vbNewLine\n\t\t\tTxt_Resul_NL.Text &amp;= $\"C = {C_Opt}\" &amp; vbNewLine &amp; vbNewLine\n\n\t\t\t' ---------------------------------------------------\n\t\t\t' 7. CALCULAR P(calc) y actualizar la tabla\n\t\t\t' ---------------------------------------------------\n\t\t\tA = A_Opt\n\t\t\tB = B_Opt\n\t\t\tC = C_Opt\n\t\t\tAPIcalc()\n\n\t\t\t' ---------------------------------------------------\n\t\t\t' 8. GRAFICAR RESULTADOS EN Chart2\n\t\t\t'     Se grafican las columnas \"P psia\" y \"P(calc) psia\"\n\t\t\t' ---------------------------------------------------\n\t\t\tChart2.Series.Clear()\n\t\t\tChart2.Legends.Clear()\n\t\t\tChart2.Legends.Add(\"\")\n\t\t\tChart2.Titles.Clear()\n\t\t\tChart2.Titles.Add(\"\")\n\t\t\tChart2.Titles(0).Font = New Font(\"Microsoft Sans Serif\", 12, FontStyle.Bold)\n\n\t\t\t' ColIndex=1 => \"P psia\"\n\t\t\t' ColIndex=2 => \"P(calc) psia\"\n\t\t\tFor colIndex As Integer = 1 To 2\n\t\t\t\tChart2.Series.Add(dt_api_NL.Columns(colIndex).ColumnName)\n\t\t\t\tWith Chart2.Series(Chart2.Series.Count - 1)\n\t\t\t\t\t.MarkerStyle = DataVisualization.Charting.MarkerStyle.Square\n\t\t\t\t\t.IsVisibleInLegend = True\n\t\t\t\t\t.ChartType = DataVisualization.Charting.SeriesChartType.Line\n\t\t\t\t\t.BorderWidth = 2\n\t\t\t\t\t.ToolTip = \"#SERIESNAME | T: #VALX{f2} P: #VALY{f2}\"\n\n\t\t\t\t\tFor j = 0 To rowCount - 1\n\t\t\t\t\t\tDim Tval = CDbl(dt_api_NL.Rows(j)(\"Temp \u00baF\"))\n\t\t\t\t\t\tDim Pval = CDbl(dt_api_NL.Rows(j)(colIndex))\n\t\t\t\t\t\t.Points.AddXY(Tval, Pval)\n\t\t\t\t\tNext\n\t\t\t\tEnd With\n\t\t\tNext\n\n\t\t\t' Ajustes de ejes\n\t\t\tChart2.ChartAreas(0).AxisX.Title = \"Temperatura (\u00baF)\"\n\t\t\tChart2.ChartAreas(0).AxisY.Title = \"Presi\u00f3n de vapor (psia)\"\n\t\t\tChart2.ChartAreas(0).AxisX.LabelStyle.Format = \"#\"\n\t\t\tChart2.ChartAreas(0).AxisY.LabelStyle.Format = \"#\"\n\t\t\t' Recalcular ejes\n\t\t\tChart2.ChartAreas(0).RecalculateAxesScale()\n\n\t\tCatch ex As Exception\n\t\t\tMsgBox(ex.ToString)\n\t\tEnd Try\n\tEnd Sub<\/pre>\n\n\n\n<h2 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"Comparativa\"><\/span>Comparativa<span class=\"ez-toc-section-end\"><\/span><\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Ambos c\u00f3digos utilizan el algoritmo de optimizaci\u00f3n <strong>Levenberg-Marquardt<\/strong>, una <strong>tolerancia <\/strong>de error de 1e-6 y tienen fijado un m\u00e1ximo de iteraciones de 1000.<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><tbody><tr><td class=\"has-text-align-right\" data-align=\"right\"><\/td><td class=\"has-text-align-center\" data-align=\"center\">Iteraciones con<br>A=2 B=1500 C=273<\/td><td class=\"has-text-align-center\" data-align=\"center\">Iteraciones con<br>A=100 B=100 C=100<\/td><td class=\"has-text-align-center\" data-align=\"center\">Iteraciones con <br>A=0 B=0 C=0<\/td><\/tr><tr><td class=\"has-text-align-right\" data-align=\"right\">Accord.NET<\/td><td class=\"has-text-align-center\" data-align=\"center\">45<\/td><td class=\"has-text-align-center\" data-align=\"center\">No soluciona<\/td><td class=\"has-text-align-center\" data-align=\"center\">149<\/td><\/tr><tr><td class=\"has-text-align-right\" data-align=\"right\">Math.NET Numerics<\/td><td class=\"has-text-align-center\" data-align=\"center\">47<\/td><td class=\"has-text-align-center\" data-align=\"center\">812<\/td><td class=\"has-text-align-center\" data-align=\"center\">82<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"Conclusiones\"><\/span>Conclusiones<span class=\"ez-toc-section-end\"><\/span><\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Las dos librer\u00edas han logrado encontrar el mejor resultado posible casi sin esfuerzo, ahora bien, Math.NET Numerics gana por goleada en facilidad de uso e implementaci\u00f3n. Excepto usando valores negativos en los que ambos algoritmos han fallado, el algoritmo de minimizaci\u00f3n de Math.NET ha resultado mucho m\u00e1s \u00f3ptimo. Otro punto en contra de Accord.Net es que parece que su desarrollo se ha estancado, siendo su \u00faltimo lanzamiento en 2017 cuando Math.NET Numerics sigue en continuo desarrollo en 2025. Esto no quiere decir que Accord.NET sea una librer\u00eda a desechar, ya que, incluso tiene m\u00e1s funcionalidades que Math.NET Numerics, aunque en mi humilde opini\u00f3n la discontinuidad y la falta de comunidad siempre te lastran.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"Enlaces\"><\/span>Enlaces<span class=\"ez-toc-section-end\"><\/span><\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Accord.NET [<a href=\"https:\/\/github.com\/accord-net\/framework\">Github<\/a>] [<a href=\"http:\/\/accord-framework.net\/\" target=\"_blank\" rel=\"noreferrer noopener\">Doc<\/a>]<\/li>\n\n\n\n<li>Math.NET Numerics [<a href=\"https:\/\/github.com\/mathnet\/mathnet-numerics\">Github<\/a>] [<a href=\"https:\/\/numerics.mathdotnet.com\/\" target=\"_blank\" rel=\"noreferrer noopener\">Doc<\/a>]<\/li>\n\n\n\n<li>Fuentes VS2019 [<a href=\"https:\/\/garikoitz.info\/blog\/descargas\/Antoine_Accord_MathNumerics.7z\">garikoitz.info<\/a>]<\/li>\n\n\n\n<li>Ecuaci\u00f3n de Antoine [<a href=\"https:\/\/es.wikipedia.org\/wiki\/Ecuaci%C3%B3n_de_Antoine\" target=\"_blank\" rel=\"noreferrer noopener\">Wikipedia<\/a>]<\/li>\n\n\n\n<li>Algoritmo Levenberg-Marquardt [<a href=\"https:\/\/es.wikipedia.org\/wiki\/Algoritmo_de_Levenberg-Marquardt\" target=\"_blank\" rel=\"noreferrer noopener\">Wikipedia<\/a>]<\/li>\n\n\n\n<li>Regresi\u00f3n lineal y no lineal &#8211; Excel vs NET [<a href=\"https:\/\/garikoitz.info\/blog\/2021\/06\/regresion-lineal-y-no-lineal-excel-vs-net\/\" target=\"_blank\" rel=\"noreferrer noopener\">garikoitz.info<\/a>]<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\"><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Recientemente he estado trabajando bastante con la librer&iacute;a Math.NET Numerics, y debo decir que me ha dejado muy buen sabor de boca. Es por ello que he decidido volver a plantear el problema de regresi&oacute;n no lineal, originalmente abordado en la entrada &ldquo;Regresi&oacute;n lineal y no lineal &ndash; Excel vs NET&rdquo;, para realizar una comparativa entre la ya conocida Accord y la reciente incorporaci&oacute;n de Math.NET Numerics. En esta ocasi&oacute;n ir&eacute; directo al grano y os presentar&eacute; ambas implementaciones en&#8230;<\/p>\n<p class=\"read-more\"><a class=\"btn btn-default\" href=\"https:\/\/garikoitz.info\/blog\/2025\/03\/regresion-no-lineal-accord-net-vs-math-net-numerics\/\"> Leer m\u00e1s<span class=\"screen-reader-text\">  Leer m\u00e1s<\/span><\/a><\/p>\n","protected":false},"author":2,"featured_media":2409,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_crdt_document":"","wpupg_custom_link":[],"wpupg_custom_link_behaviour":[],"wpupg_custom_link_nofollow":[],"wpupg_custom_image":[],"wpupg_custom_image_id":[],"footnotes":""},"categories":[37,221],"tags":[110,209,231,230,111],"class_list":["post-2382","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-net","category-matematicas","tag-accord-net","tag-algoritmo","tag-levenberg-marquardt","tag-math-net-numerics","tag-regresion"],"_links":{"self":[{"href":"https:\/\/garikoitz.info\/blog\/wp-json\/wp\/v2\/posts\/2382","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/garikoitz.info\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/garikoitz.info\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/garikoitz.info\/blog\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/garikoitz.info\/blog\/wp-json\/wp\/v2\/comments?post=2382"}],"version-history":[{"count":24,"href":"https:\/\/garikoitz.info\/blog\/wp-json\/wp\/v2\/posts\/2382\/revisions"}],"predecessor-version":[{"id":2408,"href":"https:\/\/garikoitz.info\/blog\/wp-json\/wp\/v2\/posts\/2382\/revisions\/2408"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/garikoitz.info\/blog\/wp-json\/wp\/v2\/media\/2409"}],"wp:attachment":[{"href":"https:\/\/garikoitz.info\/blog\/wp-json\/wp\/v2\/media?parent=2382"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/garikoitz.info\/blog\/wp-json\/wp\/v2\/categories?post=2382"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/garikoitz.info\/blog\/wp-json\/wp\/v2\/tags?post=2382"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}