easeLP / index.html
aymnsk's picture
Update index.html
770544e verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Complex 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">Complex 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>