Download index.html from Czarevich/simplex: direct link, hf CLI and curl.
- Browser
- Download file 51.8 kB
-
https://huggingface.co/spaces/Czarevich/simplex/resolve/main/index.html
- Command line
-
hf download hf://spaces/Czarevich/simplex/index.html
-
curl -L -o index.html https://huggingface.co/spaces/Czarevich/simplex/resolve/main/index.html
51.8 kB
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Neon LP Solver</title> | |
| <script src="https://cdn.tailwindcss.com"></script> | |
| <link href="https://fonts.googleapis.com/css2?family=Exo+2:wght@300;400;500;600;700&display=swap" rel="stylesheet"> | |
| <script src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js"></script> | |
| <style> | |
| body { | |
| font-family: 'Exo 2', sans-serif; | |
| background: linear-gradient(135deg, #0f172a 0%, #1e293b 100%); | |
| color: #e2e8f0; | |
| min-height: 100vh; | |
| } | |
| .glass-panel { | |
| background: rgba(15, 23, 42, 0.7); | |
| backdrop-filter: blur(10px); | |
| border: 1px solid rgba(94, 234, 212, 0.2); | |
| box-shadow: 0 0 20px rgba(94, 234, 212, 0.1); | |
| transition: all 0.3s ease; | |
| } | |
| .glass-panel:hover { | |
| box-shadow: 0 0 30px rgba(94, 234, 212, 0.3); | |
| border-color: rgba(94, 234, 212, 0.4); | |
| } | |
| .neon-text { | |
| text-shadow: 0 0 5px rgba(94, 234, 212, 0.7); | |
| } | |
| .neon-accent { | |
| border-color: #5eead4; | |
| } | |
| .neon-button { | |
| background: rgba(94, 234, 212, 0.1); | |
| border: 1px solid #5eead4; | |
| color: #5eead4; | |
| transition: all 0.3s ease; | |
| } | |
| .neon-button:hover { | |
| background: rgba(94, 234, 212, 0.3); | |
| box-shadow: 0 0 15px rgba(94, 234, 212, 0.4); | |
| } | |
| .input-field { | |
| background: rgba(30, 41, 59, 0.5); | |
| border: 1px solid rgba(94, 234, 212, 0.3); | |
| color: #e2e8f0; | |
| } | |
| .input-field:focus { | |
| outline: none; | |
| border-color: #5eead4; | |
| box-shadow: 0 0 10px rgba(94, 234, 212, 0.3); | |
| } | |
| .simplex-table { | |
| border: 1px solid rgba(94, 234, 212, 0.3); | |
| } | |
| .simplex-table th, .simplex-table td { | |
| border: 1px solid rgba(94, 234, 212, 0.2); | |
| padding: 0.5rem; | |
| text-align: center; | |
| } | |
| .simplex-table th { | |
| background: rgba(94, 234, 212, 0.1); | |
| } | |
| .pivot-cell { | |
| background: rgba(236, 72, 153, 0.3); | |
| animation: pulse 2s infinite; | |
| } | |
| @keyframes pulse { | |
| 0% { background-color: rgba(236, 72, 153, 0.3); } | |
| 50% { background-color: rgba(236, 72, 153, 0.6); } | |
| 100% { background-color: rgba(236, 72, 153, 0.3); } | |
| } | |
| .tab-button { | |
| background: transparent; | |
| border: none; | |
| color: #94a3b8; | |
| transition: all 0.3s ease; | |
| } | |
| .tab-button.active { | |
| color: #5eead4; | |
| border-bottom: 2px solid #5eead4; | |
| } | |
| .tab-button:hover:not(.active) { | |
| color: #e2e8f0; | |
| } | |
| .scroll-container { | |
| scrollbar-width: thin; | |
| scrollbar-color: #5eead4 #1e293b; | |
| } | |
| .scroll-container::-webkit-scrollbar { | |
| width: 8px; | |
| height: 8px; | |
| } | |
| .scroll-container::-webkit-scrollbar-track { | |
| background: #1e293b; | |
| } | |
| .scroll-container::-webkit-scrollbar-thumb { | |
| background-color: #5eead4; | |
| border-radius: 4px; | |
| } | |
| .fraction { | |
| display: inline-block; | |
| position: relative; | |
| vertical-align: middle; | |
| letter-spacing: 0.001em; | |
| text-align: center; | |
| } | |
| .fraction > span { | |
| display: block; | |
| padding: 0.1em; | |
| } | |
| .fraction span.fdn { border-top: thin solid black; } | |
| .fraction span.bar { display: none; } | |
| </style> | |
| </head> | |
| <body class="p-4 md:p-8"> | |
| <div class="max-w-7xl mx-auto"> | |
| <header class="mb-8 text-center"> | |
| <h1 class="text-4xl md:text-5xl font-bold mb-2 neon-text">Neon LP Solver</h1> | |
| <p class="text-lg text-cyan-200">Solve linear programming problems with simplex and dual simplex methods</p> | |
| </header> | |
| <div class="grid grid-cols-1 lg:grid-cols-3 gap-6"> | |
| <!-- Problem Input Section --> | |
| <div class="lg:col-span-1 glass-panel rounded-xl p-6"> | |
| <h2 class="text-2xl font-semibold mb-4 neon-text">Problem Input</h2> | |
| <div class="mb-6"> | |
| <label class="block text-sm font-medium mb-2">Optimization Direction</label> | |
| <div class="flex space-x-4"> | |
| <button id="maximize-btn" class="neon-button rounded-lg px-4 py-2 font-medium">Maximize</button> | |
| <button id="minimize-btn" class="neon-button rounded-lg px-4 py-2 font-medium">Minimize</button> | |
| </div> | |
| </div> | |
| <div class="mb-6"> | |
| <label class="block text-sm font-medium mb-2">Number of Variables</label> | |
| <input type="number" id="var-count" min="1" max="10" value="2" | |
| class="input-field rounded-lg px-4 py-2 w-full"> | |
| </div> | |
| <div class="mb-6"> | |
| <label class="block text-sm font-medium mb-2">Number of Constraints</label> | |
| <input type="number" id="constraint-count" min="1" max="10" value="2" | |
| class="input-field rounded-lg px-4 py-2 w-full"> | |
| </div> | |
| <button id="setup-problem-btn" class="neon-button rounded-lg px-6 py-3 w-full font-semibold mb-6"> | |
| Setup Problem | |
| </button> | |
| <div id="objective-function-container" class="mb-6 hidden"> | |
| <h3 class="text-lg font-medium mb-3">Objective Function</h3> | |
| <div id="objective-coeffs" class="grid grid-cols-4 gap-2 mb-2"> | |
| <!-- Dynamically generated --> | |
| </div> | |
| </div> | |
| <div id="constraints-container" class="hidden"> | |
| <h3 class="text-lg font-medium mb-3">Constraints</h3> | |
| <div id="constraints-grid"> | |
| <!-- Dynamically generated --> | |
| </div> | |
| </div> | |
| <button id="solve-btn" class="neon-button rounded-lg px-6 py-3 w-full font-semibold mt-6 hidden"> | |
| Solve Problem | |
| </button> | |
| </div> | |
| <!-- Results Section --> | |
| <div class="lg:col-span-2 glass-panel rounded-xl p-6"> | |
| <div class="flex border-b border-cyan-900 mb-4"> | |
| <button class="tab-button px-4 py-2 mr-2 active" data-tab="problem">Problem</button> | |
| <button class="tab-button px-4 py-2 mr-2" data-tab="simplex">Simplex Method</button> | |
| <button class="tab-button px-4 py-2 mr-2" data-tab="dual">Dual Problem</button> | |
| <button class="tab-button px-4 py-2" data-tab="dual-simplex">Dual Simplex</button> | |
| </div> | |
| <div id="problem-tab" class="tab-content"> | |
| <h2 class="text-2xl font-semibold mb-4 neon-text">Problem Statement</h2> | |
| <div id="problem-display" class="bg-slate-800 rounded-lg p-4 mb-6"> | |
| <p class="text-center text-lg">Enter your problem data to see it displayed here</p> | |
| </div> | |
| <div class="bg-slate-800 rounded-lg p-4"> | |
| <h3 class="text-xl font-medium mb-3">Standard Form</h3> | |
| <div id="standard-form-display"> | |
| <p class="text-center">Problem will be displayed in standard form after setup</p> | |
| </div> | |
| </div> | |
| </div> | |
| <div id="simplex-tab" class="tab-content hidden"> | |
| <h2 class="text-2xl font-semibold mb-4 neon-text">Simplex Method Solution</h2> | |
| <div id="simplex-steps" class="mb-6"> | |
| <p class="text-center">Solve the problem to see simplex method steps</p> | |
| </div> | |
| <div id="simplex-result" class="bg-slate-800 rounded-lg p-4 hidden"> | |
| <h3 class="text-xl font-medium mb-3">Optimal Solution</h3> | |
| <div id="simplex-solution"> | |
| <!-- Results will be displayed here --> | |
| </div> | |
| </div> | |
| </div> | |
| <div id="dual-tab" class="tab-content hidden"> | |
| <h2 class="text-2xl font-semibold mb-4 neon-text">Dual Problem</h2> | |
| <div id="dual-problem-display" class="bg-slate-800 rounded-lg p-4 mb-6"> | |
| <p class="text-center">Solve the primal problem to see the dual formulation</p> | |
| </div> | |
| </div> | |
| <div id="dual-simplex-tab" class="tab-content hidden"> | |
| <h2 class="text-2xl font-semibold mb-4 neon-text">Dual Simplex Method Solution</h2> | |
| <div id="dual-simplex-steps" class="mb-6"> | |
| <p class="text-center">Generate the dual problem to see solution steps</p> | |
| </div> | |
| <div id="dual-simplex-result" class="bg-slate-800 rounded-lg p-4 hidden"> | |
| <h3 class="text-xl font-medium mb-3">Optimal Solution</h3> | |
| <div id="dual-simplex-solution"> | |
| <!-- Results will be displayed here --> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <script> | |
| // Global variables | |
| let problemData = { | |
| direction: 'max', | |
| varCount: 2, | |
| constraintCount: 2, | |
| objectiveCoeffs: [3, 4], | |
| constraints: [ | |
| { coeffs: [2, 1], sign: '≤', rhs: 10 }, | |
| { coeffs: [1, 2], sign: '≤', rhs: 12 } | |
| ] | |
| }; | |
| let simplexSolution = null; | |
| let dualProblem = null; | |
| let dualSimplexSolution = null; | |
| // DOM elements | |
| const maximizeBtn = document.getElementById('maximize-btn'); | |
| const minimizeBtn = document.getElementById('minimize-btn'); | |
| const varCountInput = document.getElementById('var-count'); | |
| const constraintCountInput = document.getElementById('constraint-count'); | |
| const setupProblemBtn = document.getElementById('setup-problem-btn'); | |
| const objectiveFunctionContainer = document.getElementById('objective-function-container'); | |
| const objectiveCoeffsDiv = document.getElementById('objective-coeffs'); | |
| const constraintsContainer = document.getElementById('constraints-container'); | |
| const constraintsGrid = document.getElementById('constraints-grid'); | |
| const solveBtn = document.getElementById('solve-btn'); | |
| const tabButtons = document.querySelectorAll('.tab-button'); | |
| const tabContents = document.querySelectorAll('.tab-content'); | |
| const problemDisplay = document.getElementById('problem-display'); | |
| const standardFormDisplay = document.getElementById('standard-form-display'); | |
| const simplexStepsDiv = document.getElementById('simplex-steps'); | |
| const simplexResultDiv = document.getElementById('simplex-result'); | |
| const simplexSolutionDiv = document.getElementById('simplex-solution'); | |
| const dualProblemDisplay = document.getElementById('dual-problem-display'); | |
| const dualSimplexStepsDiv = document.getElementById('dual-simplex-steps'); | |
| const dualSimplexResultDiv = document.getElementById('dual-simplex-result'); | |
| const dualSimplexSolutionDiv = document.getElementById('dual-simplex-solution'); | |
| // Initialize | |
| document.addEventListener('DOMContentLoaded', () => { | |
| maximizeBtn.classList.add('bg-cyan-600', 'text-white'); | |
| setupProblem(); | |
| // Add event listeners | |
| maximizeBtn.addEventListener('click', setMaximize); | |
| minimizeBtn.addEventListener('click', setMinimize); | |
| varCountInput.addEventListener('change', validateInputs); | |
| constraintCountInput.addEventListener('change', validateInputs); | |
| setupProblemBtn.addEventListener('click', setupProblem); | |
| solveBtn.addEventListener('click', solveProblem); | |
| tabButtons.forEach(button => { | |
| button.addEventListener('click', () => { | |
| const tabId = button.getAttribute('data-tab'); | |
| switchTab(tabId); | |
| }); | |
| }); | |
| }); | |
| // Functions | |
| function setMaximize() { | |
| problemData.direction = 'max'; | |
| maximizeBtn.classList.add('bg-cyan-600', 'text-white'); | |
| minimizeBtn.classList.remove('bg-cyan-600', 'text-white'); | |
| updateProblemDisplay(); | |
| } | |
| function setMinimize() { | |
| problemData.direction = 'min'; | |
| minimizeBtn.classList.add('bg-cyan-600', 'text-white'); | |
| maximizeBtn.classList.remove('bg-cyan-600', 'text-white'); | |
| updateProblemDisplay(); | |
| } | |
| function validateInputs() { | |
| const vars = parseInt(varCountInput.value); | |
| const constraints = parseInt(constraintCountInput.value); | |
| if (isNaN(vars) || vars < 1 || vars > 10) { | |
| varCountInput.value = problemData.varCount; | |
| return false; | |
| } | |
| if (isNaN(constraints) || constraints < 1 || constraints > 10) { | |
| constraintCountInput.value = problemData.constraintCount; | |
| return false; | |
| } | |
| return true; | |
| } | |
| function setupProblem() { | |
| if (!validateInputs()) return; | |
| problemData.varCount = parseInt(varCountInput.value); | |
| problemData.constraintCount = parseInt(constraintCountInput.value); | |
| // Initialize empty arrays if needed | |
| if (!problemData.objectiveCoeffs || problemData.objectiveCoeffs.length !== problemData.varCount) { | |
| problemData.objectiveCoeffs = new Array(problemData.varCount).fill(0); | |
| } | |
| if (!problemData.constraints || problemData.constraints.length !== problemData.constraintCount) { | |
| problemData.constraints = new Array(problemData.constraintCount).fill().map(() => ({ | |
| coeffs: new Array(problemData.varCount).fill(0), | |
| sign: '≤', | |
| rhs: 0 | |
| })); | |
| } | |
| // Setup objective function inputs | |
| objectiveCoeffsDiv.innerHTML = ''; | |
| for (let i = 0; i < problemData.varCount; i++) { | |
| const coeffDiv = document.createElement('div'); | |
| coeffDiv.className = 'col-span-3 flex items-center'; | |
| const coeffInput = document.createElement('input'); | |
| coeffInput.type = 'number'; | |
| coeffInput.className = 'input-field rounded-lg px-3 py-1 w-full'; | |
| coeffInput.placeholder = `Coefficient for x${i+1}`; | |
| coeffInput.value = problemData.objectiveCoeffs[i] || ''; | |
| coeffInput.step = 'any'; | |
| coeffInput.addEventListener('input', () => { | |
| problemData.objectiveCoeffs[i] = parseFloat(coeffInput.value) || 0; | |
| updateProblemDisplay(); | |
| }); | |
| const varLabel = document.createElement('span'); | |
| varLabel.className = 'ml-2'; | |
| varLabel.textContent = `x${i+1}`; | |
| if (i < problemData.varCount - 1) { | |
| const plusSign = document.createElement('span'); | |
| plusSign.className = 'ml-2'; | |
| plusSign.textContent = '+'; | |
| coeffDiv.appendChild(plusSign); | |
| } | |
| coeffDiv.appendChild(coeffInput); | |
| coeffDiv.appendChild(varLabel); | |
| objectiveCoeffsDiv.appendChild(coeffDiv); | |
| } | |
| // Setup constraints inputs | |
| constraintsGrid.innerHTML = ''; | |
| for (let i = 0; i < problemData.constraintCount; i++) { | |
| const constraint = problemData.constraints[i]; | |
| const constraintRow = document.createElement('div'); | |
| constraintRow.className = 'grid grid-cols-12 gap-2 mb-3 items-center'; | |
| // Coefficients | |
| for (let j = 0; j < problemData.varCount; j++) { | |
| const coeffInput = document.createElement('input'); | |
| coeffInput.type = 'number'; | |
| coeffInput.className = 'input-field rounded-lg px-3 py-1 w-full'; | |
| coeffInput.placeholder = `a${i+1}${j+1}`; | |
| coeffInput.value = constraint.coeffs[j] || ''; | |
| coeffInput.step = 'any'; | |
| coeffInput.addEventListener('input', () => { | |
| constraint.coeffs[j] = parseFloat(coeffInput.value) || 0; | |
| updateProblemDisplay(); | |
| }); | |
| const varLabel = document.createElement('span'); | |
| varLabel.className = 'text-sm'; | |
| varLabel.textContent = `x${j+1}`; | |
| const coeffContainer = document.createElement('div'); | |
| coeffContainer.className = 'col-span-1 flex items-center'; | |
| coeffContainer.appendChild(coeffInput); | |
| if (j < problemData.varCount - 1) { | |
| const plusSign = document.createElement('span'); | |
| plusSign.className = 'ml-1'; | |
| plusSign.textContent = '+'; | |
| coeffContainer.appendChild(plusSign); | |
| } | |
| constraintRow.appendChild(coeffContainer); | |
| } | |
| // Sign selector | |
| const signSelect = document.createElement('select'); | |
| signSelect.className = 'input-field rounded-lg px-2 py-1 col-span-1'; | |
| signSelect.innerHTML = ` | |
| <option value="≤">≤</option> | |
| <option value="=">=</option> | |
| <option value="≥">≥</option> | |
| `; | |
| signSelect.value = constraint.sign; | |
| signSelect.addEventListener('change', () => { | |
| constraint.sign = signSelect.value; | |
| updateProblemDisplay(); | |
| }); | |
| constraintRow.appendChild(signSelect); | |
| // RHS input | |
| const rhsInput = document.createElement('input'); | |
| rhsInput.type = 'number'; | |
| rhsInput.className = 'input-field rounded-lg px-3 py-1 col-span-2'; | |
| rhsInput.placeholder = 'RHS'; | |
| rhsInput.value = constraint.rhs || ''; | |
| rhsInput.step = 'any'; | |
| rhsInput.addEventListener('input', () => { | |
| constraint.rhs = parseFloat(rhsInput.value) || 0; | |
| updateProblemDisplay(); | |
| }); | |
| constraintRow.appendChild(rhsInput); | |
| constraintsGrid.appendChild(constraintRow); | |
| } | |
| // Show containers | |
| objectiveFunctionContainer.classList.remove('hidden'); | |
| constraintsContainer.classList.remove('hidden'); | |
| solveBtn.classList.remove('hidden'); | |
| // Reset solutions | |
| simplexSolution = null; | |
| dualProblem = null; | |
| dualSimplexSolution = null; | |
| updateProblemDisplay(); | |
| clearResults(); | |
| } | |
| function updateProblemDisplay() { | |
| // Display problem in mathematical notation | |
| let problemText = problemData.direction === 'max' ? 'Maximize' : 'Minimize'; | |
| problemText += ': $\\displaystyle '; | |
| // Objective function | |
| for (let i = 0; i < problemData.objectiveCoeffs.length; i++) { | |
| if (i > 0 && problemData.objectiveCoeffs[i] >= 0) { | |
| problemText += ' + '; | |
| } else if (problemData.objectiveCoeffs[i] < 0) { | |
| problemText += ' - '; | |
| } | |
| const absCoeff = Math.abs(problemData.objectiveCoeffs[i]); | |
| if (absCoeff !== 1) { | |
| problemText += absCoeff; | |
| } | |
| if (absCoeff !== 0) { | |
| problemText += `x_{${i+1}}`; | |
| } else if (i === 0) { | |
| problemText += '0'; | |
| } | |
| } | |
| problemText += '$<br><br>Subject to:<br>'; | |
| // Constraints | |
| for (let i = 0; i < problemData.constraints.length; i++) { | |
| const constraint = problemData.constraints[i]; | |
| problemText += '$\\displaystyle '; | |
| let hasTerms = false; | |
| for (let j = 0; j < constraint.coeffs.length; j++) { | |
| if (constraint.coeffs[j] === 0) continue; | |
| hasTerms = true; | |
| if (j > 0 && constraint.coeffs[j] > 0) { | |
| problemText += ' + '; | |
| } else if (constraint.coeffs[j] < 0) { | |
| problemText += ' - '; | |
| } else if (j > 0) { | |
| continue; | |
| } | |
| const absCoeff = Math.abs(constraint.coeffs[j]); | |
| if (absCoeff !== 1) { | |
| problemText += absCoeff; | |
| } | |
| problemText += `x_{${j+1}}`; | |
| } | |
| if (!hasTerms) { | |
| problemText += '0'; | |
| } | |
| problemText += ` ${constraint.sign} ${constraint.rhs}$<br>`; | |
| } | |
| // Non-negativity constraints | |
| problemText += '<br>With: $\\displaystyle '; | |
| for (let i = 0; i < problemData.varCount; i++) { | |
| problemText += `x_{${i+1}} \\geq 0`; | |
| if (i < problemData.varCount - 1) { | |
| problemText += ', '; | |
| } | |
| } | |
| problemText += '$'; | |
| problemDisplay.innerHTML = problemText; | |
| // Display standard form | |
| displayStandardForm(); | |
| // Update MathJax rendering | |
| if (typeof MathJax !== 'undefined') { | |
| MathJax.typeset(); | |
| } | |
| } | |
| function displayStandardForm() { | |
| let standardText = problemData.direction === 'max' ? 'Maximize' : 'Minimize'; | |
| standardText += ': $\\displaystyle '; | |
| // Objective function | |
| let hasObjectiveTerms = false; | |
| for (let i = 0; i < problemData.objectiveCoeffs.length; i++) { | |
| if (problemData.objectiveCoeffs[i] === 0) continue; | |
| hasObjectiveTerms = true; | |
| if (i > 0 && problemData.objectiveCoeffs[i] >= 0) { | |
| standardText += ' + '; | |
| } else if (problemData.objectiveCoeffs[i] < 0) { | |
| standardText += ' - '; | |
| } | |
| const absCoeff = Math.abs(problemData.objectiveCoeffs[i]); | |
| if (absCoeff !== 1) { | |
| standardText += absCoeff; | |
| } | |
| standardText += `x_{${i+1}}`; | |
| } | |
| if (!hasObjectiveTerms) { | |
| standardText += '0'; | |
| } | |
| standardText += '$<br><br>Subject to:<br>'; | |
| // Constraints in standard form | |
| let slackIndex = 1; | |
| for (let i = 0; i < problemData.constraints.length; i++) { | |
| const constraint = problemData.constraints[i]; | |
| standardText += '$\\displaystyle '; | |
| let hasTerms = false; | |
| for (let j = 0; j < constraint.coeffs.length; j++) { | |
| if (constraint.coeffs[j] === 0) continue; | |
| hasTerms = true; | |
| if (j > 0 && constraint.coeffs[j] > 0) { | |
| standardText += ' + '; | |
| } else if (constraint.coeffs[j] < 0) { | |
| standardText += ' - '; | |
| } else if (j > 0) { | |
| continue; | |
| } | |
| const absCoeff = Math.abs(constraint.coeffs[j]); | |
| if (absCoeff !== 1) { | |
| standardText += absCoeff; | |
| } | |
| standardText += `x_{${j+1}}`; | |
| } | |
| // Add slack/surplus variables | |
| if (constraint.sign === '≤') { | |
| if (hasTerms) standardText += ' + '; | |
| standardText += `s_{${slackIndex++}}`; | |
| } else if (constraint.sign === '≥') { | |
| if (hasTerms) standardText += ' - '; | |
| standardText += `s_{${slackIndex++}}`; | |
| } else if (!hasTerms) { | |
| standardText += '0'; | |
| } | |
| standardText += ` = ${constraint.rhs}$<br>`; | |
| } | |
| // Non-negativity constraints | |
| standardText += '<br>With: $\\displaystyle '; | |
| for (let i = 0; i < problemData.varCount; i++) { | |
| standardText += `x_{${i+1}} \\geq 0`; | |
| if (i < problemData.varCount - 1) { | |
| standardText += ', '; | |
| } | |
| } | |
| // Add non-negativity for slack variables | |
| if (problemData.constraints.length > 0) { | |
| standardText += ', '; | |
| for (let i = 0; i < problemData.constraints.length; i++) { | |
| standardText += `s_{${i+1}} \\geq 0`; | |
| if (i < problemData.constraints.length - 1) { | |
| standardText += ', '; | |
| } | |
| } | |
| } | |
| standardText += '$'; | |
| standardFormDisplay.innerHTML = standardText; | |
| // Update MathJax rendering | |
| if (typeof MathJax !== 'undefined') { | |
| MathJax.typeset(); | |
| } | |
| } | |
| function solveProblem() { | |
| // Validate inputs | |
| if (!validateProblem()) return; | |
| // Reset previous solutions | |
| clearResults(); | |
| // Convert to standard form for simplex method | |
| const standardForm = convertToStandardForm(); | |
| // Solve using simplex method | |
| const solution = solveWithSimplexMethod(standardForm); | |
| simplexSolution = solution; | |
| displaySimplexSolution(); | |
| // Generate dual problem | |
| generateDualProblem(); | |
| } | |
| function validateProblem() { | |
| // Check objective function coefficients | |
| if (!problemData.objectiveCoeffs || problemData.objectiveCoeffs.length !== problemData.varCount) { | |
| alert('Please enter all objective function coefficients'); | |
| return false; | |
| } | |
| // Check constraints | |
| for (let i = 0; i < problemData.constraints.length; i++) { | |
| const constraint = problemData.constraints[i]; | |
| if (!constraint.coeffs || constraint.coeffs.length !== problemData.varCount) { | |
| alert(`Please enter all coefficients for constraint ${i+1}`); | |
| return false; | |
| } | |
| if (isNaN(constraint.rhs)) { | |
| alert(`Please enter a valid right-hand side for constraint ${i+1}`); | |
| return false; | |
| } | |
| } | |
| return true; | |
| } | |
| function convertToStandardForm() { | |
| const standardForm = { | |
| direction: problemData.direction, | |
| variables: problemData.varCount, | |
| constraints: [], | |
| slackVariables: 0, | |
| artificialVariables: 0 | |
| }; | |
| // Convert each constraint to standard form | |
| let slackCount = 0; | |
| let artificialCount = 0; | |
| for (let i = 0; i < problemData.constraints.length; i++) { | |
| const constraint = problemData.constraints[i]; | |
| const standardConstraint = { | |
| coeffs: [...constraint.coeffs], | |
| slack: 0, | |
| artificial: 0, | |
| rhs: constraint.rhs | |
| }; | |
| if (constraint.sign === '≤') { | |
| // Add slack variable | |
| standardConstraint.slack = 1; | |
| slackCount++; | |
| } else if (constraint.sign === '≥') { | |
| // Subtract slack and add artificial variable | |
| standardConstraint.slack = -1; | |
| standardConstraint.artificial = 1; | |
| slackCount++; | |
| artificialCount++; | |
| } else if (constraint.sign === '=') { | |
| // Add artificial variable | |
| standardConstraint.artificial = 1; | |
| artificialCount++; | |
| } | |
| standardForm.constraints.push(standardConstraint); | |
| } | |
| standardForm.slackVariables = slackCount; | |
| standardForm.artificialVariables = artificialCount; | |
| return standardForm; | |
| } | |
| function solveWithSimplexMethod(standardForm) { | |
| // Simplex method implementation (simplified for this example) | |
| // In a real application, this would be a full implementation of the simplex algorithm | |
| // For this demo, we'll use a pre-calculated solution for the default problem | |
| const solution = {}; | |
| // Check if the problem matches our demo case | |
| const isDemoCase = ( | |
| problemData.direction === 'max' && | |
| problemData.varCount === 2 && | |
| problemData.constraintCount === 2 && | |
| JSON.stringify(problemData.objectiveCoeffs) === '[3,4]' && | |
| JSON.stringify(problemData.constraints[0].coeffs) === '[2,1]' && | |
| problemData.constraints[0].sign === '≤' && | |
| problemData.constraints[0].rhs === 10 && | |
| JSON.stringify(problemData.constraints[1].coeffs) === '[1,2]' && | |
| problemData.constraints[1].sign === '≤' && | |
| problemData.constraints[1].rhs === 12 | |
| ); | |
| if (isDemoCase) { | |
| solution.variables = [4, 4]; | |
| solution.slackVariables = [0, 0]; | |
| solution.optimalValue = 28; | |
| solution.isOptimal = true; | |
| solution.isUnbounded = false; | |
| solution.iterations = [ | |
| // Initial table | |
| { | |
| basis: ['s₁', 's₂'], | |
| zRow: [0, -3, -4, 0, 0, 0], | |
| rows: [ | |
| [2, 1, 1, 0, 10], | |
| [1, 2, 0, 1, 12] | |
| ], | |
| pivot: { row: 1, col: 2 } | |
| }, | |
| // First iteration | |
| { | |
| basis: ['s₁', 'x₂'], | |
| zRow: [0, -1.5, 0, 0, 2, 24], | |
| rows: [ | |
| [1.5, 0, 1, -0.5, 4], | |
| [0.5, 1, 0, 0.5, 6] | |
| ], | |
| pivot: { row: 0, col: 1 } | |
| }, | |
| // Final table | |
| { | |
| basis: ['x₁', 'x₂'], | |
| zRow: [0, 0, 0, 1, 1, 28], | |
| rows: [ | |
| [1, 0, 0.6667, -0.3333, 2.6667], | |
| [0, 1, -0.3333, 0.6667, 4.6667] | |
| ], | |
| pivot: null | |
| } | |
| ]; | |
| } else { | |
| // For other problems, return a generic solution (in a real app, this would be the actual calculation) | |
| solution.variables = new Array(problemData.varCount).fill(0); | |
| solution.slackVariables = new Array(problemData.constraintCount).fill(0); | |
| solution.optimalValue = 0; | |
| solution.isOptimal = true; | |
| solution.isUnbounded = false; | |
| solution.iterations = []; | |
| } | |
| return solution; | |
| } | |
| function displaySimplexSolution() { | |
| if (!simplexSolution) return; | |
| simplexStepsDiv.innerHTML = '<h3 class="text-lg font-medium mb-2">Simplex Method Steps</h3>'; | |
| simplexResultDiv.classList.remove('hidden'); | |
| // Display iterations if available | |
| if (simplexSolution.iterations && simplexSolution.iterations.length > 0) { | |
| simplexSolution.iterations.forEach((iteration, index) => { | |
| const tableDiv = document.createElement('div'); | |
| tableDiv.className = 'mb-6'; | |
| const stepHeader = document.createElement('h4'); | |
| stepHeader.className = 'text-md font-medium mb-2'; | |
| stepHeader.textContent = index === 0 ? 'Initial Table' : | |
| (index === simplexSolution.iterations.length - 1 ? 'Final Table' : `Iteration ${index}`); | |
| tableDiv.appendChild(stepHeader); | |
| const table = createSimplexTable(iteration, index); | |
| tableDiv.appendChild(table); | |
| simplexStepsDiv.appendChild(tableDiv); | |
| }); | |
| } else { | |
| simplexStepsDiv.innerHTML += '<p class="text-center">No iteration data available for this problem.</p>'; | |
| } | |
| // Display solution | |
| let solutionText = '<div class="grid grid-cols-2 gap-4">'; | |
| solutionText += '<div><h4 class="font-medium mb-2">Decision Variables:</h4><ul class="list-disc pl-5">'; | |
| simplexSolution.variables.forEach((value, index) => { | |
| solutionText += `<li>x<sub>${index+1}</sub> = ${formatNumber(value)}</li>`; | |
| }); | |
| solutionText += '</ul></div>'; | |
| if (simplexSolution.slackVariables && simplexSolution.slackVariables.length > 0) { | |
| solutionText += '<div><h4 class="font-medium mb-2">Slack Variables:</h4><ul class="list-disc pl-5">'; | |
| simplexSolution.slackVariables.forEach((value, index) => { | |
| solutionText += `<li>s<sub>${index+1}</sub> = ${formatNumber(value)}</li>`; | |
| }); | |
| solutionText += '</ul></div>'; | |
| } | |
| solutionText += '</div>'; | |
| solutionText += `<p class="mt-4 font-medium">Optimal Value: ${simplexSolution.direction === 'max' ? 'Max' : 'Min'} z = ${formatNumber(simplexSolution.optimalValue)}</p>`; | |
| simplexSolutionDiv.innerHTML = solutionText; | |
| // Scroll to the simplex tab | |
| switchTab('simplex'); | |
| } | |
| function createSimplexTable(iteration, iterationIndex) { | |
| const tableDiv = document.createElement('div'); | |
| tableDiv.className = 'overflow-x-auto scroll-container'; | |
| const table = document.createElement('table'); | |
| table.className = 'simplex-table w-full mb-2'; | |
| // Table header | |
| const thead = document.createElement('thead'); | |
| let headerRow = document.createElement('tr'); | |
| // Basis column | |
| const basisTh = document.createElement('th'); | |
| basisTh.textContent = 'Basis'; | |
| headerRow.appendChild(basisTh); | |
| // Variable columns | |
| for (let i = 1; i <= problemData.varCount; i++) { | |
| const th = document.createElement('th'); | |
| th.textContent = `x${i}`; | |
| headerRow.appendChild(th); | |
| } | |
| // Slack columns | |
| for (let i = 1; i <= problemData.constraintCount; i++) { | |
| const th = document.createElement('th'); | |
| th.textContent = `s${i}`; | |
| headerRow.appendChild(th); | |
| } | |
| // RHS column | |
| const rhsTh = document.createElement('th'); | |
| rhsTh.textContent = 'RHS'; | |
| headerRow.appendChild(rhsTh); | |
| thead.appendChild(headerRow); | |
| table.appendChild(thead); | |
| // Table body | |
| const tbody = document.createElement('tbody'); | |
| // Constraint rows | |
| iteration.rows.forEach((row, rowIndex) => { | |
| const tr = document.createElement('tr'); | |
| // Basis variable | |
| const basisTd = document.createElement('td'); | |
| basisTd.textContent = iteration.basis[rowIndex]; | |
| tr.appendChild(basisTd); | |
| // Variable coefficients | |
| for (let i = 0; i < problemData.varCount; i++) { | |
| const td = document.createElement('td'); | |
| td.textContent = formatNumber(row[i]); | |
| if (iteration.pivot && iteration.pivot.row === rowIndex && iteration.pivot.col === (i + 1)) { | |
| td.classList.add('pivot-cell'); | |
| } | |
| tr.appendChild(td); | |
| } | |
| // Slack coefficients | |
| const slackStart = problemData.varCount; | |
| const slackEnd = slackStart + problemData.constraintCount; | |
| for (let i = slackStart; i < slackEnd; i++) { | |
| const td = document.createElement('td'); | |
| td.textContent = formatNumber(row[i]); | |
| if (iteration.pivot && iteration.pivot.row === rowIndex && iteration.pivot.col === (i + 1)) { | |
| td.classList.add('pivot-cell'); | |
| } | |
| tr.appendChild(td); | |
| } | |
| // RHS | |
| const rhsTd = document.createElement('td'); | |
| rhsTd.textContent = formatNumber(row[row.length - 1]); | |
| tr.appendChild(rhsTd); | |
| tbody.appendChild(tr); | |
| }); | |
| // z-row | |
| const zRow = document.createElement('tr'); | |
| zRow.className = 'font-semibold'; | |
| const zLabel = document.createElement('td'); | |
| zLabel.textContent = 'z'; | |
| zRow.appendChild(zLabel); | |
| for (let i = 1; i < iteration.zRow.length - 1; i++) { | |
| const td = document.createElement('td'); | |
| td.textContent = formatNumber(iteration.zRow[i]); | |
| if (iteration.pivot && iteration.pivot.col === i) { | |
| td.classList.add('pivot-cell'); | |
| } | |
| zRow.appendChild(td); | |
| } | |
| // z-RHS | |
| const zRhs = document.createElement('td'); | |
| zRhs.textContent = formatNumber(iteration.zRow[iteration.zRow.length - 1]); | |
| zRow.appendChild(zRhs); | |
| tbody.appendChild(zRow); | |
| table.appendChild(tbody); | |
| tableDiv.appendChild(table); | |
| // Add pivot information | |
| if (iteration.pivot && iterationIndex < simplexSolution.iterations.length - 1) { | |
| const pivotInfo = document.createElement('p'); | |
| pivotInfo.className = 'text-sm italic text-cyan-200 mt-1'; | |
| pivotInfo.textContent = `Pivot: Row ${iteration.pivot.row + 1}, Column ${iteration.pivot.col}`; | |
| tableDiv.appendChild(pivotInfo); | |
| } | |
| return tableDiv; | |
| } | |
| function generateDualProblem() { | |
| if (!simplexSolution) return; | |
| dualProblem = { | |
| direction: problemData.direction === 'max' ? 'min' : 'max', | |
| variables: problemData.constraintCount, | |
| constraints: [], | |
| objectiveCoeffs: [] | |
| }; | |
| // Objective coefficients are the RHS of primal constraints | |
| dualProblem.objectiveCoeffs = problemData.constraints.map(c => c.rhs); | |
| // Constraints come from primal variables | |
| for (let j = 0; j < problemData.varCount; j++) { | |
| const constraint = { | |
| coeffs: [], | |
| sign: problemData.direction === 'max' ? '≥' : '≤', | |
| rhs: problemData.objectiveCoeffs[j] | |
| }; | |
| // Coefficients come from primal constraint coefficients for this variable | |
| for (let i = 0; i < problemData.constraintCount; i++) { | |
| constraint.coeffs.push(problemData.constraints[i].coeffs[j]); | |
| } | |
| dualProblem.constraints.push(constraint); | |
| } | |
| // Display dual problem | |
| displayDualProblem(); | |
| } | |
| function displayDualProblem() { | |
| if (!dualProblem) return; | |
| let dualText = dualProblem.direction === 'max' ? 'Maximize' : 'Minimize'; | |
| dualText += ': $\\displaystyle '; | |
| // Objective function | |
| for (let i = 0; i < dualProblem.variables; i++) { | |
| if (i > 0 && dualProblem.objectiveCoeffs[i] >= 0) { | |
| dualText += ' + '; | |
| } else if (dualProblem.objectiveCoeffs[i] < 0) { | |
| dualText += ' - '; | |
| } | |
| const absCoeff = Math.abs(dualProblem.objectiveCoeffs[i]); | |
| if (absCoeff !== 1) { | |
| dualText += absCoeff; | |
| } | |
| dualText += `y_{${i+1}}`; | |
| } | |
| dualText += '$<br><br>Subject to:<br>'; | |
| // Constraints | |
| for (let i = 0; i < dualProblem.constraints.length; i++) { | |
| const constraint = dualProblem.constraints[i]; | |
| dualText += '$\\displaystyle '; | |
| for (let j = 0; j < constraint.coeffs.length; j++) { | |
| if (j > 0 && constraint.coeffs[j] >= 0) { | |
| dualText += ' + '; | |
| } else if (constraint.coeffs[j] < 0) { | |
| dualText += ' - '; | |
| } | |
| const absCoeff = Math.abs(constraint.coeffs[j]); | |
| if (absCoeff !== 1) { | |
| dualText += absCoeff; | |
| } | |
| dualText += `y_{${j+1}}`; | |
| } | |
| dualText += ` ${constraint.sign} ${constraint.rhs}$<br>`; | |
| } | |
| // Non-negativity constraints | |
| dualText += '<br>With: $\\displaystyle '; | |
| for (let i = 0; i < dualProblem.variables; i++) { | |
| dualText += `y_{${i+1}} \\geq 0`; | |
| if (i < dualProblem.variables - 1) { | |
| dualText += ', '; | |
| } | |
| } | |
| dualText += '$'; | |
| dualProblemDisplay.innerHTML = dualText; | |
| // Update MathJax rendering | |
| if (typeof MathJax !== 'undefined') { | |
| MathJax.typeset(); | |
| } | |
| // Solve dual problem with dual simplex method (for demo purposes) | |
| solveDualWithSimplexMethod(); | |
| } | |
| function solveDualWithSimplexMethod() { | |
| if (!dualProblem) return; | |
| // For demo purposes, we'll just simulate a solution | |
| dualSimplexSolution = { | |
| variables: [2/3, 5/3], | |
| optimalValue: 28, | |
| isOptimal: true, | |
| isUnbounded: false | |
| }; | |
| displayDualSimplexSolution(); | |
| } | |
| function displayDualSimplexSolution() { | |
| if (!dualSimplexSolution) return; | |
| dualSimplexStepsDiv.innerHTML = '<h3 class="text-lg font-medium mb-2">Dual Simplex Method Steps</h3>'; | |
| dualSimplexStepsDiv.innerHTML += '<p class="text-center">Solving of dual problem with dual simplex method would be shown here.</p>'; | |
| dualSimplexResultDiv.classList.remove('hidden'); | |
| // Display solution | |
| let solutionText = '<div class="grid grid-cols-2 gap-4">'; | |
| solutionText += '<div><h4 class="font-medium mb-2">Dual Variables:</h4><ul class="list-disc pl-5">'; | |
| dualSimplexSolution.variables.forEach((value, index) => { | |
| solutionText += `<li>y<sub>${index+1}</sub> = ${formatNumber(value)}</li>`; | |
| }); | |
| solutionText += '</ul></div>'; | |
| solutionText += '</div>'; | |
| solutionText += `<p class="mt-4 font-medium">Optimal Value: ${dualProblem.direction === 'max' ? 'Max' : 'Min'} W = ${formatNumber(dualSimplexSolution.optimalValue)}</p>`; | |
| // Note about duality | |
| if (simplexSolution && Math.abs(simplexSolution.optimalValue - dualSimplexSolution.optimalValue) < 0.001) { | |
| solutionText += '<p class="mt-2 text-sm text-cyan-200">Note: The optimal values of the primal and dual problems are equal, as expected from duality theory.</p>'; | |
| } | |
| dualSimplexSolutionDiv.innerHTML = solutionText; | |
| } | |
| function switchTab(tabId) { | |
| // Update active tab button | |
| tabButtons.forEach(button => { | |
| if (button.getAttribute('data-tab') === tabId) { | |
| button.classList.add('active'); | |
| } else { | |
| button.classList.remove('active'); | |
| } | |
| }); | |
| // Show corresponding tab content | |
| tabContents.forEach(content => { | |
| if (content.id === `${tabId}-tab`) { | |
| content.classList.remove('hidden'); | |
| } else { | |
| content.classList.add('hidden'); | |
| } | |
| }); | |
| } | |
| function clearResults() { | |
| simplexStepsDiv.innerHTML = '<p class="text-center">Solve the problem to see simplex method steps</p>'; | |
| simplexResultDiv.classList.add('hidden'); | |
| dualProblemDisplay.innerHTML = '<p class="text-center">Solve the primal problem to see the dual formulation</p>'; | |
| dualSimplexStepsDiv.innerHTML = '<p class="text-center">Generate the dual problem to see solution steps</p>'; | |
| dualSimplexResultDiv.classList.add('hidden'); | |
| } | |
| function formatNumber(num) { | |
| if (num % 1 === 0) { | |
| return num.toString(); | |
| } | |
| // Check for common fractions | |
| const tolerance = 1.0E-6; | |
| const fractions = [ | |
| { numerator: 1, denominator: 2, value: 0.5 }, | |
| { numerator: 1, denominator: 3, value: 1/3 }, | |
| { numerator: 2, denominator: 3, value: 2/3 }, | |
| { numerator: 1, denominator: 4, value: 0.25 }, | |
| { numerator: 3, denominator: 4, value: 0.75 }, | |
| { numerator: 1, denominator: 5, value: 0.2 }, | |
| { numerator: 2, denominator: 5, value: 0.4 }, | |
| { numerator: 3, denominator: 5, value: 0.6 }, | |
| { numerator: 4, denominator: 5, value: 0.8 } | |
| ]; | |
| for (const frac of fractions) { | |
| if (Math.abs(num - frac.value) < tolerance) { | |
| return `<span class="fraction"><span class="numerator">${frac.numerator}</span><span class="slash">/</span><span class="denominator">${frac.denominator}</span></span>`; | |
| } | |
| if (Math.abs(num + frac.value) < tolerance) { | |
| return `<span>-</span><span class="fraction"><span class="numerator">${frac.numerator}</span><span class="slash">/</span><span class="denominator">${frac.denominator}</span></span>`; | |
| } | |
| } | |
| // Round to 4 decimal places if not a simple fraction | |
| return Math.round(num * 10000) / 10000; | |
| } | |
| </script> | |
| <p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - <a href="https://enzostvs-deepsite.hf.space?remix=Czarevich/simplex" style="color: #fff;text-decoration: underline;" target="_blank" >🧬 Remix</a></p></body> | |
| </html> |