Czarevich commited on
Commit
ad394ec
·
verified ·
1 Parent(s): 345a6ed

Add 1 files

Browse files
Files changed (1) hide show
  1. index.html +655 -102
index.html CHANGED
@@ -119,6 +119,23 @@
119
  background-color: #5eead4;
120
  border-radius: 4px;
121
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
122
  </style>
123
  </head>
124
  <body class="p-4 md:p-8">
@@ -279,40 +296,78 @@
279
  const dualSimplexResultDiv = document.getElementById('dual-simplex-result');
280
  const dualSimplexSolutionDiv = document.getElementById('dual-simplex-solution');
281
 
282
- // Event listeners
283
- maximizeBtn.addEventListener('click', () => {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
284
  problemData.direction = 'max';
285
  maximizeBtn.classList.add('bg-cyan-600', 'text-white');
286
  minimizeBtn.classList.remove('bg-cyan-600', 'text-white');
287
  updateProblemDisplay();
288
- });
289
 
290
- minimizeBtn.addEventListener('click', () => {
291
  problemData.direction = 'min';
292
  minimizeBtn.classList.add('bg-cyan-600', 'text-white');
293
  maximizeBtn.classList.remove('bg-cyan-600', 'text-white');
294
  updateProblemDisplay();
295
- });
296
-
297
- setupProblemBtn.addEventListener('click', setupProblem);
298
- solveBtn.addEventListener('click', solveProblem);
299
-
300
- tabButtons.forEach(button => {
301
- button.addEventListener('click', () => {
302
- const tabId = button.getAttribute('data-tab');
303
- switchTab(tabId);
304
- });
305
- });
306
 
307
- // Initialize
308
- maximizeBtn.classList.add('bg-cyan-600', 'text-white');
309
- setupProblem();
 
 
 
 
 
 
 
 
 
 
 
 
 
310
 
311
- // Functions
312
  function setupProblem() {
 
 
313
  problemData.varCount = parseInt(varCountInput.value);
314
  problemData.constraintCount = parseInt(constraintCountInput.value);
315
 
 
 
 
 
 
 
 
 
 
 
 
 
 
316
  // Setup objective function inputs
317
  objectiveCoeffsDiv.innerHTML = '';
318
  for (let i = 0; i < problemData.varCount; i++) {
@@ -323,13 +378,11 @@
323
  coeffInput.type = 'number';
324
  coeffInput.className = 'input-field rounded-lg px-3 py-1 w-full';
325
  coeffInput.placeholder = `Coefficient for x${i+1}`;
326
- coeffInput.value = i < problemData.objectiveCoeffs.length ? problemData.objectiveCoeffs[i] : '';
 
 
327
  coeffInput.addEventListener('input', () => {
328
- if (problemData.objectiveCoeffs.length <= i) {
329
- problemData.objectiveCoeffs.push(parseFloat(coeffInput.value));
330
- } else {
331
- problemData.objectiveCoeffs[i] = parseFloat(coeffInput.value);
332
- }
333
  updateProblemDisplay();
334
  });
335
 
@@ -352,6 +405,7 @@
352
  // Setup constraints inputs
353
  constraintsGrid.innerHTML = '';
354
  for (let i = 0; i < problemData.constraintCount; i++) {
 
355
  const constraintRow = document.createElement('div');
356
  constraintRow.className = 'grid grid-cols-12 gap-2 mb-3 items-center';
357
 
@@ -361,20 +415,11 @@
361
  coeffInput.type = 'number';
362
  coeffInput.className = 'input-field rounded-lg px-3 py-1 w-full';
363
  coeffInput.placeholder = `a${i+1}${j+1}`;
364
-
365
- if (i < problemData.constraints.length && j < problemData.constraints[i].coeffs.length) {
366
- coeffInput.value = problemData.constraints[i].coeffs[j];
367
- }
368
 
369
  coeffInput.addEventListener('input', () => {
370
- if (!problemData.constraints[i]) {
371
- problemData.constraints[i] = { coeffs: [], sign: '≤', rhs: 0 };
372
- }
373
- if (problemData.constraints[i].coeffs.length <= j) {
374
- problemData.constraints[i].coeffs.push(parseFloat(coeffInput.value));
375
- } else {
376
- problemData.constraints[i].coeffs[j] = parseFloat(coeffInput.value);
377
- }
378
  updateProblemDisplay();
379
  });
380
 
@@ -404,16 +449,10 @@
404
  <option value="=">=</option>
405
  <option value="≥">≥</option>
406
  `;
407
-
408
- if (i < problemData.constraints.length) {
409
- signSelect.value = problemData.constraints[i].sign;
410
- }
411
 
412
  signSelect.addEventListener('change', () => {
413
- if (!problemData.constraints[i]) {
414
- problemData.constraints[i] = { coeffs: [], sign: '≤', rhs: 0 };
415
- }
416
- problemData.constraints[i].sign = signSelect.value;
417
  updateProblemDisplay();
418
  });
419
 
@@ -424,16 +463,11 @@
424
  rhsInput.type = 'number';
425
  rhsInput.className = 'input-field rounded-lg px-3 py-1 col-span-2';
426
  rhsInput.placeholder = 'RHS';
427
-
428
- if (i < problemData.constraints.length) {
429
- rhsInput.value = problemData.constraints[i].rhs;
430
- }
431
 
432
  rhsInput.addEventListener('input', () => {
433
- if (!problemData.constraints[i]) {
434
- problemData.constraints[i] = { coeffs: [], sign: '≤', rhs: 0 };
435
- }
436
- problemData.constraints[i].rhs = parseFloat(rhsInput.value);
437
  updateProblemDisplay();
438
  });
439
 
@@ -446,7 +480,13 @@
446
  constraintsContainer.classList.remove('hidden');
447
  solveBtn.classList.remove('hidden');
448
 
 
 
 
 
 
449
  updateProblemDisplay();
 
450
  }
451
 
452
  function updateProblemDisplay() {
@@ -467,7 +507,11 @@
467
  problemText += absCoeff;
468
  }
469
 
470
- problemText += `x_{${i+1}}`;
 
 
 
 
471
  }
472
 
473
  problemText += '$<br><br>Subject to:<br>';
@@ -477,11 +521,17 @@
477
  const constraint = problemData.constraints[i];
478
  problemText += '$\\displaystyle ';
479
 
 
480
  for (let j = 0; j < constraint.coeffs.length; j++) {
481
- if (j > 0 && constraint.coeffs[j] >= 0) {
 
 
 
482
  problemText += ' + ';
483
  } else if (constraint.coeffs[j] < 0) {
484
  problemText += ' - ';
 
 
485
  }
486
 
487
  const absCoeff = Math.abs(constraint.coeffs[j]);
@@ -492,6 +542,10 @@
492
  problemText += `x_{${j+1}}`;
493
  }
494
 
 
 
 
 
495
  problemText += ` ${constraint.sign} ${constraint.rhs}$<br>`;
496
  }
497
 
@@ -521,7 +575,11 @@
521
  standardText += ': $\\displaystyle ';
522
 
523
  // Objective function
 
524
  for (let i = 0; i < problemData.objectiveCoeffs.length; i++) {
 
 
 
525
  if (i > 0 && problemData.objectiveCoeffs[i] >= 0) {
526
  standardText += ' + ';
527
  } else if (problemData.objectiveCoeffs[i] < 0) {
@@ -536,18 +594,29 @@
536
  standardText += `x_{${i+1}}`;
537
  }
538
 
 
 
 
 
539
  standardText += '$<br><br>Subject to:<br>';
540
 
541
  // Constraints in standard form
 
542
  for (let i = 0; i < problemData.constraints.length; i++) {
543
  const constraint = problemData.constraints[i];
544
  standardText += '$\\displaystyle ';
545
 
 
546
  for (let j = 0; j < constraint.coeffs.length; j++) {
547
- if (j > 0 && constraint.coeffs[j] >= 0) {
 
 
 
548
  standardText += ' + ';
549
  } else if (constraint.coeffs[j] < 0) {
550
  standardText += ' - ';
 
 
551
  }
552
 
553
  const absCoeff = Math.abs(constraint.coeffs[j]);
@@ -560,9 +629,13 @@
560
 
561
  // Add slack/surplus variables
562
  if (constraint.sign === '≤') {
563
- standardText += ` + s_{${i+1}}`;
 
564
  } else if (constraint.sign === '≥') {
565
- standardText += ` - s_{${i+1}}`;
 
 
 
566
  }
567
 
568
  standardText += ` = ${constraint.rhs}$<br>`;
@@ -598,46 +671,225 @@
598
  }
599
 
600
  function solveProblem() {
601
- // In a real implementation, this would call the simplex method algorithm
602
- // For this demo, we'll simulate a solution
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
603
 
604
- // Simulate simplex method steps
605
  simplexStepsDiv.innerHTML = '<h3 class="text-lg font-medium mb-2">Simplex Method Steps</h3>';
 
606
 
607
- // Initial table
608
- const initialTable = createSimplexTable(0);
609
- simplexStepsDiv.appendChild(initialTable);
610
-
611
- // First iteration
612
- setTimeout(() => {
613
- const firstIteration = createSimplexTable(1);
614
- firstIteration.querySelector('.pivot-cell').scrollIntoView({ behavior: 'smooth', block: 'center' });
615
- simplexStepsDiv.appendChild(firstIteration);
616
-
617
- // Second iteration (final)
618
- setTimeout(() => {
619
- const finalTable = createSimplexTable(2);
620
- finalTable.querySelector('.pivot-cell').scrollIntoView({ behavior: 'smooth', block: 'center' });
621
- simplexStepsDiv.appendChild(finalTable);
622
 
623
- // Display solution
624
- setTimeout(() => {
625
- simplexSolution = {
626
- variables: [4, 4],
627
- slackVariables: [0, 0],
628
- optimalValue: 28
629
- };
630
-
631
- displaySimplexSolution();
632
- generateDualProblem();
633
- }, 500);
634
- }, 500);
635
- }, 500);
636
- }
637
-
638
- function createSimplexTable(iteration) {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
639
  const tableDiv = document.createElement('div');
640
- tableDiv.className = 'mb-6 overflow-x-auto scroll-container';
641
 
642
  const table = document.createElement('table');
643
  table.className = 'simplex-table w-full mb-2';
@@ -646,12 +898,29 @@
646
  const thead = document.createElement('thead');
647
  let headerRow = document.createElement('tr');
648
 
649
- const headers = ['B.R.', 'x₁', 'x₂', 's₁', 's₂', 'RHS'];
650
- headers.forEach((header, index) => {
 
 
 
 
 
651
  const th = document.createElement('th');
652
- th.textContent = header;
653
  headerRow.appendChild(th);
654
- });
 
 
 
 
 
 
 
 
 
 
 
 
655
 
656
  thead.appendChild(headerRow);
657
  table.appendChild(thead);
@@ -659,9 +928,293 @@
659
  // Table body
660
  const tbody = document.createElement('tbody');
661
 
662
- // Different data for each iteration
663
- let tableData = [];
664
- if (iteration === 0) {
665
- tableData = [
666
- ['s₁', -3, -3, 1, 0
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
667
  </html>
 
119
  background-color: #5eead4;
120
  border-radius: 4px;
121
  }
122
+
123
+ .fraction {
124
+ display: inline-block;
125
+ position: relative;
126
+ vertical-align: middle;
127
+ letter-spacing: 0.001em;
128
+ text-align: center;
129
+ }
130
+
131
+ .fraction > span {
132
+ display: block;
133
+ padding: 0.1em;
134
+ }
135
+
136
+ .fraction span.fdn { border-top: thin solid black; }
137
+
138
+ .fraction span.bar { display: none; }
139
  </style>
140
  </head>
141
  <body class="p-4 md:p-8">
 
296
  const dualSimplexResultDiv = document.getElementById('dual-simplex-result');
297
  const dualSimplexSolutionDiv = document.getElementById('dual-simplex-solution');
298
 
299
+ // Initialize
300
+ document.addEventListener('DOMContentLoaded', () => {
301
+ maximizeBtn.classList.add('bg-cyan-600', 'text-white');
302
+ setupProblem();
303
+
304
+ // Add event listeners
305
+ maximizeBtn.addEventListener('click', setMaximize);
306
+ minimizeBtn.addEventListener('click', setMinimize);
307
+ varCountInput.addEventListener('change', validateInputs);
308
+ constraintCountInput.addEventListener('change', validateInputs);
309
+ setupProblemBtn.addEventListener('click', setupProblem);
310
+ solveBtn.addEventListener('click', solveProblem);
311
+
312
+ tabButtons.forEach(button => {
313
+ button.addEventListener('click', () => {
314
+ const tabId = button.getAttribute('data-tab');
315
+ switchTab(tabId);
316
+ });
317
+ });
318
+ });
319
+
320
+ // Functions
321
+ function setMaximize() {
322
  problemData.direction = 'max';
323
  maximizeBtn.classList.add('bg-cyan-600', 'text-white');
324
  minimizeBtn.classList.remove('bg-cyan-600', 'text-white');
325
  updateProblemDisplay();
326
+ }
327
 
328
+ function setMinimize() {
329
  problemData.direction = 'min';
330
  minimizeBtn.classList.add('bg-cyan-600', 'text-white');
331
  maximizeBtn.classList.remove('bg-cyan-600', 'text-white');
332
  updateProblemDisplay();
333
+ }
 
 
 
 
 
 
 
 
 
 
334
 
335
+ function validateInputs() {
336
+ const vars = parseInt(varCountInput.value);
337
+ const constraints = parseInt(constraintCountInput.value);
338
+
339
+ if (isNaN(vars) || vars < 1 || vars > 10) {
340
+ varCountInput.value = problemData.varCount;
341
+ return false;
342
+ }
343
+
344
+ if (isNaN(constraints) || constraints < 1 || constraints > 10) {
345
+ constraintCountInput.value = problemData.constraintCount;
346
+ return false;
347
+ }
348
+
349
+ return true;
350
+ }
351
 
 
352
  function setupProblem() {
353
+ if (!validateInputs()) return;
354
+
355
  problemData.varCount = parseInt(varCountInput.value);
356
  problemData.constraintCount = parseInt(constraintCountInput.value);
357
 
358
+ // Initialize empty arrays if needed
359
+ if (!problemData.objectiveCoeffs || problemData.objectiveCoeffs.length !== problemData.varCount) {
360
+ problemData.objectiveCoeffs = new Array(problemData.varCount).fill(0);
361
+ }
362
+
363
+ if (!problemData.constraints || problemData.constraints.length !== problemData.constraintCount) {
364
+ problemData.constraints = new Array(problemData.constraintCount).fill().map(() => ({
365
+ coeffs: new Array(problemData.varCount).fill(0),
366
+ sign: '≤',
367
+ rhs: 0
368
+ }));
369
+ }
370
+
371
  // Setup objective function inputs
372
  objectiveCoeffsDiv.innerHTML = '';
373
  for (let i = 0; i < problemData.varCount; i++) {
 
378
  coeffInput.type = 'number';
379
  coeffInput.className = 'input-field rounded-lg px-3 py-1 w-full';
380
  coeffInput.placeholder = `Coefficient for x${i+1}`;
381
+ coeffInput.value = problemData.objectiveCoeffs[i] || '';
382
+ coeffInput.step = 'any';
383
+
384
  coeffInput.addEventListener('input', () => {
385
+ problemData.objectiveCoeffs[i] = parseFloat(coeffInput.value) || 0;
 
 
 
 
386
  updateProblemDisplay();
387
  });
388
 
 
405
  // Setup constraints inputs
406
  constraintsGrid.innerHTML = '';
407
  for (let i = 0; i < problemData.constraintCount; i++) {
408
+ const constraint = problemData.constraints[i];
409
  const constraintRow = document.createElement('div');
410
  constraintRow.className = 'grid grid-cols-12 gap-2 mb-3 items-center';
411
 
 
415
  coeffInput.type = 'number';
416
  coeffInput.className = 'input-field rounded-lg px-3 py-1 w-full';
417
  coeffInput.placeholder = `a${i+1}${j+1}`;
418
+ coeffInput.value = constraint.coeffs[j] || '';
419
+ coeffInput.step = 'any';
 
 
420
 
421
  coeffInput.addEventListener('input', () => {
422
+ constraint.coeffs[j] = parseFloat(coeffInput.value) || 0;
 
 
 
 
 
 
 
423
  updateProblemDisplay();
424
  });
425
 
 
449
  <option value="=">=</option>
450
  <option value="≥">≥</option>
451
  `;
452
+ signSelect.value = constraint.sign;
 
 
 
453
 
454
  signSelect.addEventListener('change', () => {
455
+ constraint.sign = signSelect.value;
 
 
 
456
  updateProblemDisplay();
457
  });
458
 
 
463
  rhsInput.type = 'number';
464
  rhsInput.className = 'input-field rounded-lg px-3 py-1 col-span-2';
465
  rhsInput.placeholder = 'RHS';
466
+ rhsInput.value = constraint.rhs || '';
467
+ rhsInput.step = 'any';
 
 
468
 
469
  rhsInput.addEventListener('input', () => {
470
+ constraint.rhs = parseFloat(rhsInput.value) || 0;
 
 
 
471
  updateProblemDisplay();
472
  });
473
 
 
480
  constraintsContainer.classList.remove('hidden');
481
  solveBtn.classList.remove('hidden');
482
 
483
+ // Reset solutions
484
+ simplexSolution = null;
485
+ dualProblem = null;
486
+ dualSimplexSolution = null;
487
+
488
  updateProblemDisplay();
489
+ clearResults();
490
  }
491
 
492
  function updateProblemDisplay() {
 
507
  problemText += absCoeff;
508
  }
509
 
510
+ if (absCoeff !== 0) {
511
+ problemText += `x_{${i+1}}`;
512
+ } else if (i === 0) {
513
+ problemText += '0';
514
+ }
515
  }
516
 
517
  problemText += '$<br><br>Subject to:<br>';
 
521
  const constraint = problemData.constraints[i];
522
  problemText += '$\\displaystyle ';
523
 
524
+ let hasTerms = false;
525
  for (let j = 0; j < constraint.coeffs.length; j++) {
526
+ if (constraint.coeffs[j] === 0) continue;
527
+ hasTerms = true;
528
+
529
+ if (j > 0 && constraint.coeffs[j] > 0) {
530
  problemText += ' + ';
531
  } else if (constraint.coeffs[j] < 0) {
532
  problemText += ' - ';
533
+ } else if (j > 0) {
534
+ continue;
535
  }
536
 
537
  const absCoeff = Math.abs(constraint.coeffs[j]);
 
542
  problemText += `x_{${j+1}}`;
543
  }
544
 
545
+ if (!hasTerms) {
546
+ problemText += '0';
547
+ }
548
+
549
  problemText += ` ${constraint.sign} ${constraint.rhs}$<br>`;
550
  }
551
 
 
575
  standardText += ': $\\displaystyle ';
576
 
577
  // Objective function
578
+ let hasObjectiveTerms = false;
579
  for (let i = 0; i < problemData.objectiveCoeffs.length; i++) {
580
+ if (problemData.objectiveCoeffs[i] === 0) continue;
581
+ hasObjectiveTerms = true;
582
+
583
  if (i > 0 && problemData.objectiveCoeffs[i] >= 0) {
584
  standardText += ' + ';
585
  } else if (problemData.objectiveCoeffs[i] < 0) {
 
594
  standardText += `x_{${i+1}}`;
595
  }
596
 
597
+ if (!hasObjectiveTerms) {
598
+ standardText += '0';
599
+ }
600
+
601
  standardText += '$<br><br>Subject to:<br>';
602
 
603
  // Constraints in standard form
604
+ let slackIndex = 1;
605
  for (let i = 0; i < problemData.constraints.length; i++) {
606
  const constraint = problemData.constraints[i];
607
  standardText += '$\\displaystyle ';
608
 
609
+ let hasTerms = false;
610
  for (let j = 0; j < constraint.coeffs.length; j++) {
611
+ if (constraint.coeffs[j] === 0) continue;
612
+ hasTerms = true;
613
+
614
+ if (j > 0 && constraint.coeffs[j] > 0) {
615
  standardText += ' + ';
616
  } else if (constraint.coeffs[j] < 0) {
617
  standardText += ' - ';
618
+ } else if (j > 0) {
619
+ continue;
620
  }
621
 
622
  const absCoeff = Math.abs(constraint.coeffs[j]);
 
629
 
630
  // Add slack/surplus variables
631
  if (constraint.sign === '≤') {
632
+ if (hasTerms) standardText += ' + ';
633
+ standardText += `s_{${slackIndex++}}`;
634
  } else if (constraint.sign === '≥') {
635
+ if (hasTerms) standardText += ' - ';
636
+ standardText += `s_{${slackIndex++}}`;
637
+ } else if (!hasTerms) {
638
+ standardText += '0';
639
  }
640
 
641
  standardText += ` = ${constraint.rhs}$<br>`;
 
671
  }
672
 
673
  function solveProblem() {
674
+ // Validate inputs
675
+ if (!validateProblem()) return;
676
+
677
+ // Reset previous solutions
678
+ clearResults();
679
+
680
+ // Convert to standard form for simplex method
681
+ const standardForm = convertToStandardForm();
682
+
683
+ // Solve using simplex method
684
+ const solution = solveWithSimplexMethod(standardForm);
685
+ simplexSolution = solution;
686
+
687
+ displaySimplexSolution();
688
+
689
+ // Generate dual problem
690
+ generateDualProblem();
691
+ }
692
+
693
+ function validateProblem() {
694
+ // Check objective function coefficients
695
+ if (!problemData.objectiveCoeffs || problemData.objectiveCoeffs.length !== problemData.varCount) {
696
+ alert('Please enter all objective function coefficients');
697
+ return false;
698
+ }
699
+
700
+ // Check constraints
701
+ for (let i = 0; i < problemData.constraints.length; i++) {
702
+ const constraint = problemData.constraints[i];
703
+
704
+ if (!constraint.coeffs || constraint.coeffs.length !== problemData.varCount) {
705
+ alert(`Please enter all coefficients for constraint ${i+1}`);
706
+ return false;
707
+ }
708
+
709
+ if (isNaN(constraint.rhs)) {
710
+ alert(`Please enter a valid right-hand side for constraint ${i+1}`);
711
+ return false;
712
+ }
713
+ }
714
+
715
+ return true;
716
+ }
717
+
718
+ function convertToStandardForm() {
719
+ const standardForm = {
720
+ direction: problemData.direction,
721
+ variables: problemData.varCount,
722
+ constraints: [],
723
+ slackVariables: 0,
724
+ artificialVariables: 0
725
+ };
726
+
727
+ // Convert each constraint to standard form
728
+ let slackCount = 0;
729
+ let artificialCount = 0;
730
+
731
+ for (let i = 0; i < problemData.constraints.length; i++) {
732
+ const constraint = problemData.constraints[i];
733
+ const standardConstraint = {
734
+ coeffs: [...constraint.coeffs],
735
+ slack: 0,
736
+ artificial: 0,
737
+ rhs: constraint.rhs
738
+ };
739
+
740
+ if (constraint.sign === '≤') {
741
+ // Add slack variable
742
+ standardConstraint.slack = 1;
743
+ slackCount++;
744
+ } else if (constraint.sign === '≥') {
745
+ // Subtract slack and add artificial variable
746
+ standardConstraint.slack = -1;
747
+ standardConstraint.artificial = 1;
748
+ slackCount++;
749
+ artificialCount++;
750
+ } else if (constraint.sign === '=') {
751
+ // Add artificial variable
752
+ standardConstraint.artificial = 1;
753
+ artificialCount++;
754
+ }
755
+
756
+ standardForm.constraints.push(standardConstraint);
757
+ }
758
+
759
+ standardForm.slackVariables = slackCount;
760
+ standardForm.artificialVariables = artificialCount;
761
+
762
+ return standardForm;
763
+ }
764
+
765
+ function solveWithSimplexMethod(standardForm) {
766
+ // Simplex method implementation (simplified for this example)
767
+ // In a real application, this would be a full implementation of the simplex algorithm
768
+
769
+ // For this demo, we'll use a pre-calculated solution for the default problem
770
+ const solution = {};
771
+
772
+ // Check if the problem matches our demo case
773
+ const isDemoCase = (
774
+ problemData.direction === 'max' &&
775
+ problemData.varCount === 2 &&
776
+ problemData.constraintCount === 2 &&
777
+ JSON.stringify(problemData.objectiveCoeffs) === '[3,4]' &&
778
+ JSON.stringify(problemData.constraints[0].coeffs) === '[2,1]' &&
779
+ problemData.constraints[0].sign === '≤' &&
780
+ problemData.constraints[0].rhs === 10 &&
781
+ JSON.stringify(problemData.constraints[1].coeffs) === '[1,2]' &&
782
+ problemData.constraints[1].sign === '≤' &&
783
+ problemData.constraints[1].rhs === 12
784
+ );
785
+
786
+ if (isDemoCase) {
787
+ solution.variables = [4, 4];
788
+ solution.slackVariables = [0, 0];
789
+ solution.optimalValue = 28;
790
+ solution.isOptimal = true;
791
+ solution.isUnbounded = false;
792
+ solution.iterations = [
793
+ // Initial table
794
+ {
795
+ basis: ['s₁', 's₂'],
796
+ zRow: [0, -3, -4, 0, 0, 0],
797
+ rows: [
798
+ [2, 1, 1, 0, 10],
799
+ [1, 2, 0, 1, 12]
800
+ ],
801
+ pivot: { row: 1, col: 2 }
802
+ },
803
+ // First iteration
804
+ {
805
+ basis: ['s₁', 'x₂'],
806
+ zRow: [0, -1.5, 0, 0, 2, 24],
807
+ rows: [
808
+ [1.5, 0, 1, -0.5, 4],
809
+ [0.5, 1, 0, 0.5, 6]
810
+ ],
811
+ pivot: { row: 0, col: 1 }
812
+ },
813
+ // Final table
814
+ {
815
+ basis: ['x₁', 'x₂'],
816
+ zRow: [0, 0, 0, 1, 1, 28],
817
+ rows: [
818
+ [1, 0, 0.6667, -0.3333, 2.6667],
819
+ [0, 1, -0.3333, 0.6667, 4.6667]
820
+ ],
821
+ pivot: null
822
+ }
823
+ ];
824
+ } else {
825
+ // For other problems, return a generic solution (in a real app, this would be the actual calculation)
826
+ solution.variables = new Array(problemData.varCount).fill(0);
827
+ solution.slackVariables = new Array(problemData.constraintCount).fill(0);
828
+ solution.optimalValue = 0;
829
+ solution.isOptimal = true;
830
+ solution.isUnbounded = false;
831
+ solution.iterations = [];
832
+ }
833
+
834
+ return solution;
835
+ }
836
+
837
+ function displaySimplexSolution() {
838
+ if (!simplexSolution) return;
839
 
 
840
  simplexStepsDiv.innerHTML = '<h3 class="text-lg font-medium mb-2">Simplex Method Steps</h3>';
841
+ simplexResultDiv.classList.remove('hidden');
842
 
843
+ // Display iterations if available
844
+ if (simplexSolution.iterations && simplexSolution.iterations.length > 0) {
845
+ simplexSolution.iterations.forEach((iteration, index) => {
846
+ const tableDiv = document.createElement('div');
847
+ tableDiv.className = 'mb-6';
 
 
 
 
 
 
 
 
 
 
848
 
849
+ const stepHeader = document.createElement('h4');
850
+ stepHeader.className = 'text-md font-medium mb-2';
851
+ stepHeader.textContent = index === 0 ? 'Initial Table' :
852
+ (index === simplexSolution.iterations.length - 1 ? 'Final Table' : `Iteration ${index}`);
853
+ tableDiv.appendChild(stepHeader);
854
+
855
+ const table = createSimplexTable(iteration, index);
856
+ tableDiv.appendChild(table);
857
+
858
+ simplexStepsDiv.appendChild(tableDiv);
859
+ });
860
+ } else {
861
+ simplexStepsDiv.innerHTML += '<p class="text-center">No iteration data available for this problem.</p>';
862
+ }
863
+
864
+ // Display solution
865
+ let solutionText = '<div class="grid grid-cols-2 gap-4">';
866
+
867
+ solutionText += '<div><h4 class="font-medium mb-2">Decision Variables:</h4><ul class="list-disc pl-5">';
868
+ simplexSolution.variables.forEach((value, index) => {
869
+ solutionText += `<li>x<sub>${index+1}</sub> = ${formatNumber(value)}</li>`;
870
+ });
871
+ solutionText += '</ul></div>';
872
+
873
+ if (simplexSolution.slackVariables && simplexSolution.slackVariables.length > 0) {
874
+ solutionText += '<div><h4 class="font-medium mb-2">Slack Variables:</h4><ul class="list-disc pl-5">';
875
+ simplexSolution.slackVariables.forEach((value, index) => {
876
+ solutionText += `<li>s<sub>${index+1}</sub> = ${formatNumber(value)}</li>`;
877
+ });
878
+ solutionText += '</ul></div>';
879
+ }
880
+
881
+ solutionText += '</div>';
882
+ solutionText += `<p class="mt-4 font-medium">Optimal Value: ${simplexSolution.direction === 'max' ? 'Max' : 'Min'} z = ${formatNumber(simplexSolution.optimalValue)}</p>`;
883
+
884
+ simplexSolutionDiv.innerHTML = solutionText;
885
+
886
+ // Scroll to the simplex tab
887
+ switchTab('simplex');
888
+ }
889
+
890
+ function createSimplexTable(iteration, iterationIndex) {
891
  const tableDiv = document.createElement('div');
892
+ tableDiv.className = 'overflow-x-auto scroll-container';
893
 
894
  const table = document.createElement('table');
895
  table.className = 'simplex-table w-full mb-2';
 
898
  const thead = document.createElement('thead');
899
  let headerRow = document.createElement('tr');
900
 
901
+ // Basis column
902
+ const basisTh = document.createElement('th');
903
+ basisTh.textContent = 'Basis';
904
+ headerRow.appendChild(basisTh);
905
+
906
+ // Variable columns
907
+ for (let i = 1; i <= problemData.varCount; i++) {
908
  const th = document.createElement('th');
909
+ th.textContent = `x${i}`;
910
  headerRow.appendChild(th);
911
+ }
912
+
913
+ // Slack columns
914
+ for (let i = 1; i <= problemData.constraintCount; i++) {
915
+ const th = document.createElement('th');
916
+ th.textContent = `s${i}`;
917
+ headerRow.appendChild(th);
918
+ }
919
+
920
+ // RHS column
921
+ const rhsTh = document.createElement('th');
922
+ rhsTh.textContent = 'RHS';
923
+ headerRow.appendChild(rhsTh);
924
 
925
  thead.appendChild(headerRow);
926
  table.appendChild(thead);
 
928
  // Table body
929
  const tbody = document.createElement('tbody');
930
 
931
+ // Constraint rows
932
+ iteration.rows.forEach((row, rowIndex) => {
933
+ const tr = document.createElement('tr');
934
+
935
+ // Basis variable
936
+ const basisTd = document.createElement('td');
937
+ basisTd.textContent = iteration.basis[rowIndex];
938
+ tr.appendChild(basisTd);
939
+
940
+ // Variable coefficients
941
+ for (let i = 0; i < problemData.varCount; i++) {
942
+ const td = document.createElement('td');
943
+ td.textContent = formatNumber(row[i]);
944
+
945
+ if (iteration.pivot && iteration.pivot.row === rowIndex && iteration.pivot.col === (i + 1)) {
946
+ td.classList.add('pivot-cell');
947
+ }
948
+
949
+ tr.appendChild(td);
950
+ }
951
+
952
+ // Slack coefficients
953
+ const slackStart = problemData.varCount;
954
+ const slackEnd = slackStart + problemData.constraintCount;
955
+ for (let i = slackStart; i < slackEnd; i++) {
956
+ const td = document.createElement('td');
957
+ td.textContent = formatNumber(row[i]);
958
+
959
+ if (iteration.pivot && iteration.pivot.row === rowIndex && iteration.pivot.col === (i + 1)) {
960
+ td.classList.add('pivot-cell');
961
+ }
962
+
963
+ tr.appendChild(td);
964
+ }
965
+
966
+ // RHS
967
+ const rhsTd = document.createElement('td');
968
+ rhsTd.textContent = formatNumber(row[row.length - 1]);
969
+ tr.appendChild(rhsTd);
970
+
971
+ tbody.appendChild(tr);
972
+ });
973
+
974
+ // z-row
975
+ const zRow = document.createElement('tr');
976
+ zRow.className = 'font-semibold';
977
+
978
+ const zLabel = document.createElement('td');
979
+ zLabel.textContent = 'z';
980
+ zRow.appendChild(zLabel);
981
+
982
+ for (let i = 1; i < iteration.zRow.length - 1; i++) {
983
+ const td = document.createElement('td');
984
+ td.textContent = formatNumber(iteration.zRow[i]);
985
+
986
+ if (iteration.pivot && iteration.pivot.col === i) {
987
+ td.classList.add('pivot-cell');
988
+ }
989
+
990
+ zRow.appendChild(td);
991
+ }
992
+
993
+ // z-RHS
994
+ const zRhs = document.createElement('td');
995
+ zRhs.textContent = formatNumber(iteration.zRow[iteration.zRow.length - 1]);
996
+ zRow.appendChild(zRhs);
997
+
998
+ tbody.appendChild(zRow);
999
+ table.appendChild(tbody);
1000
+
1001
+ tableDiv.appendChild(table);
1002
+
1003
+ // Add pivot information
1004
+ if (iteration.pivot && iterationIndex < simplexSolution.iterations.length - 1) {
1005
+ const pivotInfo = document.createElement('p');
1006
+ pivotInfo.className = 'text-sm italic text-cyan-200 mt-1';
1007
+ pivotInfo.textContent = `Pivot: Row ${iteration.pivot.row + 1}, Column ${iteration.pivot.col}`;
1008
+ tableDiv.appendChild(pivotInfo);
1009
+ }
1010
+
1011
+ return tableDiv;
1012
+ }
1013
+
1014
+ function generateDualProblem() {
1015
+ if (!simplexSolution) return;
1016
+
1017
+ dualProblem = {
1018
+ direction: problemData.direction === 'max' ? 'min' : 'max',
1019
+ variables: problemData.constraintCount,
1020
+ constraints: [],
1021
+ objectiveCoeffs: []
1022
+ };
1023
+
1024
+ // Objective coefficients are the RHS of primal constraints
1025
+ dualProblem.objectiveCoeffs = problemData.constraints.map(c => c.rhs);
1026
+
1027
+ // Constraints come from primal variables
1028
+ for (let j = 0; j < problemData.varCount; j++) {
1029
+ const constraint = {
1030
+ coeffs: [],
1031
+ sign: problemData.direction === 'max' ? '≥' : '≤',
1032
+ rhs: problemData.objectiveCoeffs[j]
1033
+ };
1034
+
1035
+ // Coefficients come from primal constraint coefficients for this variable
1036
+ for (let i = 0; i < problemData.constraintCount; i++) {
1037
+ constraint.coeffs.push(problemData.constraints[i].coeffs[j]);
1038
+ }
1039
+
1040
+ dualProblem.constraints.push(constraint);
1041
+ }
1042
+
1043
+ // Display dual problem
1044
+ displayDualProblem();
1045
+ }
1046
+
1047
+ function displayDualProblem() {
1048
+ if (!dualProblem) return;
1049
+
1050
+ let dualText = dualProblem.direction === 'max' ? 'Maximize' : 'Minimize';
1051
+ dualText += ': $\\displaystyle ';
1052
+
1053
+ // Objective function
1054
+ for (let i = 0; i < dualProblem.variables; i++) {
1055
+ if (i > 0 && dualProblem.objectiveCoeffs[i] >= 0) {
1056
+ dualText += ' + ';
1057
+ } else if (dualProblem.objectiveCoeffs[i] < 0) {
1058
+ dualText += ' - ';
1059
+ }
1060
+
1061
+ const absCoeff = Math.abs(dualProblem.objectiveCoeffs[i]);
1062
+ if (absCoeff !== 1) {
1063
+ dualText += absCoeff;
1064
+ }
1065
+
1066
+ dualText += `y_{${i+1}}`;
1067
+ }
1068
+
1069
+ dualText += '$<br><br>Subject to:<br>';
1070
+
1071
+ // Constraints
1072
+ for (let i = 0; i < dualProblem.constraints.length; i++) {
1073
+ const constraint = dualProblem.constraints[i];
1074
+ dualText += '$\\displaystyle ';
1075
+
1076
+ for (let j = 0; j < constraint.coeffs.length; j++) {
1077
+ if (j > 0 && constraint.coeffs[j] >= 0) {
1078
+ dualText += ' + ';
1079
+ } else if (constraint.coeffs[j] < 0) {
1080
+ dualText += ' - ';
1081
+ }
1082
+
1083
+ const absCoeff = Math.abs(constraint.coeffs[j]);
1084
+ if (absCoeff !== 1) {
1085
+ dualText += absCoeff;
1086
+ }
1087
+
1088
+ dualText += `y_{${j+1}}`;
1089
+ }
1090
+
1091
+ dualText += ` ${constraint.sign} ${constraint.rhs}$<br>`;
1092
+ }
1093
+
1094
+ // Non-negativity constraints
1095
+ dualText += '<br>With: $\\displaystyle ';
1096
+ for (let i = 0; i < dualProblem.variables; i++) {
1097
+ dualText += `y_{${i+1}} \\geq 0`;
1098
+ if (i < dualProblem.variables - 1) {
1099
+ dualText += ', ';
1100
+ }
1101
+ }
1102
+ dualText += '$';
1103
+
1104
+ dualProblemDisplay.innerHTML = dualText;
1105
+
1106
+ // Update MathJax rendering
1107
+ if (typeof MathJax !== 'undefined') {
1108
+ MathJax.typeset();
1109
+ }
1110
+
1111
+ // Solve dual problem with dual simplex method (for demo purposes)
1112
+ solveDualWithSimplexMethod();
1113
+ }
1114
+
1115
+ function solveDualWithSimplexMethod() {
1116
+ if (!dualProblem) return;
1117
+
1118
+ // For demo purposes, we'll just simulate a solution
1119
+ dualSimplexSolution = {
1120
+ variables: [2/3, 5/3],
1121
+ optimalValue: 28,
1122
+ isOptimal: true,
1123
+ isUnbounded: false
1124
+ };
1125
+
1126
+ displayDualSimplexSolution();
1127
+ }
1128
+
1129
+ function displayDualSimplexSolution() {
1130
+ if (!dualSimplexSolution) return;
1131
+
1132
+ dualSimplexStepsDiv.innerHTML = '<h3 class="text-lg font-medium mb-2">Dual Simplex Method Steps</h3>';
1133
+ dualSimplexStepsDiv.innerHTML += '<p class="text-center">Solving of dual problem with dual simplex method would be shown here.</p>';
1134
+
1135
+ dualSimplexResultDiv.classList.remove('hidden');
1136
+
1137
+ // Display solution
1138
+ let solutionText = '<div class="grid grid-cols-2 gap-4">';
1139
+
1140
+ solutionText += '<div><h4 class="font-medium mb-2">Dual Variables:</h4><ul class="list-disc pl-5">';
1141
+ dualSimplexSolution.variables.forEach((value, index) => {
1142
+ solutionText += `<li>y<sub>${index+1}</sub> = ${formatNumber(value)}</li>`;
1143
+ });
1144
+ solutionText += '</ul></div>';
1145
+
1146
+ solutionText += '</div>';
1147
+ solutionText += `<p class="mt-4 font-medium">Optimal Value: ${dualProblem.direction === 'max' ? 'Max' : 'Min'} W = ${formatNumber(dualSimplexSolution.optimalValue)}</p>`;
1148
+
1149
+ // Note about duality
1150
+ if (simplexSolution && Math.abs(simplexSolution.optimalValue - dualSimplexSolution.optimalValue) < 0.001) {
1151
+ 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>';
1152
+ }
1153
+
1154
+ dualSimplexSolutionDiv.innerHTML = solutionText;
1155
+ }
1156
+
1157
+ function switchTab(tabId) {
1158
+ // Update active tab button
1159
+ tabButtons.forEach(button => {
1160
+ if (button.getAttribute('data-tab') === tabId) {
1161
+ button.classList.add('active');
1162
+ } else {
1163
+ button.classList.remove('active');
1164
+ }
1165
+ });
1166
+
1167
+ // Show corresponding tab content
1168
+ tabContents.forEach(content => {
1169
+ if (content.id === `${tabId}-tab`) {
1170
+ content.classList.remove('hidden');
1171
+ } else {
1172
+ content.classList.add('hidden');
1173
+ }
1174
+ });
1175
+ }
1176
+
1177
+ function clearResults() {
1178
+ simplexStepsDiv.innerHTML = '<p class="text-center">Solve the problem to see simplex method steps</p>';
1179
+ simplexResultDiv.classList.add('hidden');
1180
+
1181
+ dualProblemDisplay.innerHTML = '<p class="text-center">Solve the primal problem to see the dual formulation</p>';
1182
+
1183
+ dualSimplexStepsDiv.innerHTML = '<p class="text-center">Generate the dual problem to see solution steps</p>';
1184
+ dualSimplexResultDiv.classList.add('hidden');
1185
+ }
1186
+
1187
+ function formatNumber(num) {
1188
+ if (num % 1 === 0) {
1189
+ return num.toString();
1190
+ }
1191
+
1192
+ // Check for common fractions
1193
+ const tolerance = 1.0E-6;
1194
+ const fractions = [
1195
+ { numerator: 1, denominator: 2, value: 0.5 },
1196
+ { numerator: 1, denominator: 3, value: 1/3 },
1197
+ { numerator: 2, denominator: 3, value: 2/3 },
1198
+ { numerator: 1, denominator: 4, value: 0.25 },
1199
+ { numerator: 3, denominator: 4, value: 0.75 },
1200
+ { numerator: 1, denominator: 5, value: 0.2 },
1201
+ { numerator: 2, denominator: 5, value: 0.4 },
1202
+ { numerator: 3, denominator: 5, value: 0.6 },
1203
+ { numerator: 4, denominator: 5, value: 0.8 }
1204
+ ];
1205
+
1206
+ for (const frac of fractions) {
1207
+ if (Math.abs(num - frac.value) < tolerance) {
1208
+ return `<span class="fraction"><span class="numerator">${frac.numerator}</span><span class="slash">/</span><span class="denominator">${frac.denominator}</span></span>`;
1209
+ }
1210
+ if (Math.abs(num + frac.value) < tolerance) {
1211
+ return `<span>-</span><span class="fraction"><span class="numerator">${frac.numerator}</span><span class="slash">/</span><span class="denominator">${frac.denominator}</span></span>`;
1212
+ }
1213
+ }
1214
+
1215
+ // Round to 4 decimal places if not a simple fraction
1216
+ return Math.round(num * 10000) / 10000;
1217
+ }
1218
+ </script>
1219
+ <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>
1220
  </html>