pedrobritto-123 commited on
Commit
5555137
·
verified ·
1 Parent(s): 46796ce

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +10 -567
app.py CHANGED
@@ -86,58 +86,6 @@ def parse_constraints(text: str, nvars: int) -> Tuple[List[Dict[str,Any]], List[
86
 
87
  return cons, sorted(list(set(free_vars)))
88
 
89
- """def parse_constraints(text: str, nvars: int) -> Tuple[List[Dict[str,Any]], List[int]]:
90
- lines = [ln.strip() for ln in text.strip().splitlines() if ln.strip()]
91
- skip_words = ["tal que", "sujeito a", "subject to", "s.t.", "st:"]
92
- lines = [ln for ln in lines if not any(word in ln.lower() for word in skip_words)]
93
-
94
- free_vars = []
95
- cons = []
96
- pattern_free = re.compile(r'x([0-9]+)\s*(livre|free)', flags=re.I)
97
- term_pattern = r'([+-]?[0-9./]*)(x[0-9]+)'
98
-
99
- for ln in lines[:]:
100
- m = pattern_free.search(ln)
101
- if m:
102
- idx = int(m.group(1)) - 1
103
- if idx < 0 or idx >= nvars:
104
- raise ValueError(f"Variável livre fora do intervalo: x{idx+1}")
105
- free_vars.append(idx)
106
- lines.remove(ln)
107
-
108
- for ln in lines:
109
- s = ln.replace(" ", "")
110
- if "<=" in s or "=<" in s:
111
- s = s.replace("=<", "<=")
112
- left, right = s.split("<=")
113
- sense = "<="
114
- elif ">=" in s or "=>" in s:
115
- s = s.replace("=>", ">=")
116
- left, right = s.split(">=")
117
- sense = ">="
118
- elif "=" in s:
119
- left, right = s.split("=")
120
- sense = "="
121
- else:
122
- raise ValueError(f"Faltando <=, >= ou =: '{ln}'")
123
- try:
124
- rhs = float(eval(right))
125
- except Exception:
126
- raise ValueError(f"RHS inválido em: '{ln}'")
127
- coeffs = [0.0] * nvars
128
- terms = re.findall(term_pattern, left)
129
- for coef_str, var_str in terms:
130
- idx = int(var_str[1:]) - 1
131
- if coef_str in ["", "+"]:
132
- v = 1.0
133
- elif coef_str == "-":
134
- v = -1.0
135
- else:
136
- v = float(eval(coef_str))
137
- coeffs[idx] += v
138
- cons.append({'coeffs': coeffs, 'sense': sense, 'rhs': rhs})
139
- return cons, sorted(list(set(free_vars)))"""
140
-
141
  def expand_free_variables(nvars: int, c: List[float], constraints: List[Dict[str,Any]], free_vars: List[int]):
142
  new_c = []
143
  mapping = {}
@@ -278,12 +226,8 @@ def build_tableau_two_phase(c: List[float], constraints: List[Dict[str,Any]], se
278
  return T, basis, (n, slacks, artificials), art_positions, c_adj
279
 
280
 
281
- def run_two_phase(c, constraints, sense='max'):
282
-
283
- #Implementação 100% por tableau — compatível com HuggingFace
284
- #Phase I + Phase II completas (sem SciPy).
285
-
286
- # ---------- PHASE I ----------
287
  T0, basis0, (n_orig, n_slack, n_art), art_positions, c_adj = build_tableau_two_phase(
288
  c, constraints, sense
289
  )
@@ -313,7 +257,7 @@ def run_two_phase(c, constraints, sense='max'):
313
  old_ncols = T1.shape[1] - 1
314
  keep_cols = [j for j in range(old_ncols) if j not in art_cols]
315
 
316
- # construir tableau da Phase II (T2)
317
  T2 = np.zeros((T1.shape[0], len(keep_cols) + 1))
318
  for i, col in enumerate(keep_cols):
319
  T2[:, i] = T1[:, col]
@@ -348,7 +292,7 @@ def run_two_phase(c, constraints, sense='max'):
348
  if not replaced:
349
  basis2[i] = None
350
 
351
- # ---------- PHASE II — definir objetivo original ----------
352
  c_full = []
353
  for col in keep_cols:
354
  if col < len(c_adj):
@@ -376,7 +320,7 @@ def run_two_phase(c, constraints, sense='max'):
376
  used.add(j)
377
  break
378
 
379
- # ---------- SIMPLEX PHASE II ----------
380
  try:
381
  T_final, basis_final, path2 = primal_simplex_tableau(T2.copy(), basis2.copy())
382
  except Exception as e:
@@ -387,7 +331,7 @@ def run_two_phase(c, constraints, sense='max'):
387
  'trace': traceback.format_exc()
388
  }
389
 
390
- # ---------- EXTRAI X*, REDUCED COSTS E DUAL (GERAL) ----------
391
  x = [0.0] * n_orig
392
 
393
  for i, bi in enumerate(basis_final):
@@ -503,11 +447,11 @@ def run_algorithms(nvars_str, objective_str, cons_str, sense, mode):
503
  status = res.get('status')
504
  # infeasible detected in Phase I
505
  if status == 'infeasible':
506
- return f"Problema inviável (Phase I obj = {res.get('phase1_obj')})", '', '', '', ''
507
 
508
  # Phase I failed
509
  if status == 'phase1_failed':
510
- return f"Erro na Phase I: {res.get('error','(sem detalhe)')}", '', '', '', ''
511
 
512
  if status == 'optimal':
513
  x_primal = res['x']
@@ -561,7 +505,7 @@ def run_algorithms(nvars_str, objective_str, cons_str, sense, mode):
561
  # ---------------- Gradio UI ----------------
562
 
563
  with gr.Blocks() as demo:
564
- gr.Markdown("# Simplex — Duas Fases (Phase I / Phase II) (Dual Geral)")
565
  with gr.Row():
566
  with gr.Column(scale=1):
567
  nvars = gr.Textbox(label='Número de variáveis (n)', value='2')
@@ -583,505 +527,4 @@ with gr.Blocks() as demo:
583
 
584
  if __name__ == '__main__':
585
  demo.launch(ssr_mode=False)
586
-
587
-
588
- """import re
589
- import traceback
590
- from typing import List, Dict, Any, Tuple
591
- import numpy as np
592
- import pandas as pd
593
- import gradio as gr
594
- from fpdf import FPDF
595
-
596
- EPS = 1e-9
597
-
598
- # ---------------- Parsing utilities ----------------
599
-
600
- def parse_coeffs(text: str) -> List[float]:
601
- if not text or not text.strip():
602
- return []
603
- s = text.replace(',', ' ')
604
- parts = [p for p in s.split() if p.strip()]
605
- coeffs = []
606
- for p in parts:
607
- try:
608
- coeffs.append(float(eval(p)))
609
- except Exception:
610
- raise ValueError(f"Coeficiente inválido: '{p}'")
611
- return coeffs
612
-
613
- def parse_constraints(text: str, nvars: int) -> Tuple[List[Dict[str,Any]], List[int]]:
614
- lines = [ln.strip() for ln in text.strip().splitlines() if ln.strip()]
615
- skip_words = ["tal que", "sujeito a", "subject to", "s.t.", "st:"]
616
- lines = [ln for ln in lines if not any(word in ln.lower() for word in skip_words)]
617
-
618
- free_vars = []
619
- cons = []
620
- pattern_free = re.compile(r'x([0-9]+)\s*(livre|free)', flags=re.I)
621
- term_pattern = r'([+-]?[0-9./]*)(x[0-9]+)'
622
-
623
- for ln in lines[:]:
624
- m = pattern_free.search(ln)
625
- if m:
626
- idx = int(m.group(1)) - 1
627
- if idx < 0 or idx >= nvars:
628
- raise ValueError(f"Variável livre fora do intervalo: x{idx+1}")
629
- free_vars.append(idx)
630
- lines.remove(ln)
631
-
632
- for ln in lines:
633
- s = ln.replace(" ", "")
634
- if "<=" in s or "=<" in s:
635
- s = s.replace("=<", "<=")
636
- left, right = s.split("<=")
637
- sense = "<="
638
- elif ">=" in s or "=>" in s:
639
- s = s.replace("=>", ">=")
640
- left, right = s.split(">=")
641
- sense = ">="
642
- elif "=" in s:
643
- left, right = s.split("=")
644
- sense = "="
645
- else:
646
- raise ValueError(f"Faltando <=, >= ou =: '{ln}'")
647
- try:
648
- rhs = float(eval(right))
649
- except Exception:
650
- raise ValueError(f"RHS inválido em: '{ln}'")
651
- coeffs = [0.0] * nvars
652
- terms = re.findall(term_pattern, left)
653
- for coef_str, var_str in terms:
654
- idx = int(var_str[1:]) - 1
655
- if coef_str in ["", "+"]:
656
- v = 1.0
657
- elif coef_str == "-":
658
- v = -1.0
659
- else:
660
- v = float(eval(coef_str))
661
- coeffs[idx] += v
662
- cons.append({'coeffs': coeffs, 'sense': sense, 'rhs': rhs})
663
- return cons, sorted(list(set(free_vars)))
664
-
665
- def expand_free_variables(nvars: int, c: List[float], constraints: List[Dict[str,Any]], free_vars: List[int]):
666
- new_c = []
667
- mapping = {}
668
- for i in range(nvars):
669
- if i in free_vars:
670
- new_c.append(c[i]); mapping[len(new_c)-1] = (i, +1)
671
- new_c.append(-c[i]); mapping[len(new_c)-1] = (i, -1)
672
- else:
673
- new_c.append(c[i]); mapping[len(new_c)-1] = (i, +1)
674
- new_constraints = []
675
- for row in constraints:
676
- coeffs = row['coeffs']
677
- new_coeffs = []
678
- for i in range(nvars):
679
- if i in free_vars:
680
- new_coeffs.append(coeffs[i])
681
- new_coeffs.append(-coeffs[i])
682
- else:
683
- new_coeffs.append(coeffs[i])
684
- new_constraints.append({'coeffs': new_coeffs, 'sense': row['sense'], 'rhs': row['rhs']})
685
- return len(new_c), new_c, new_constraints, mapping
686
-
687
- # ---------------- Tableau helpers ----------------
688
-
689
- def snapshot_html(tableau: np.ndarray, basis: List[int]) -> str:
690
- cols = tableau.shape[1]
691
- html = '<table border="1" style="border-collapse:collapse;font-family:Arial; font-size:12px;">'
692
- for i in range(tableau.shape[0]):
693
- html += '<tr>'
694
- for j in range(cols):
695
- val = tableau[i, j]
696
- html += f'<td style="padding:4px;">{val:.6g}</td>'
697
- html += '</tr>'
698
- html += '</table>'
699
- return html
700
-
701
- def primal_simplex_tableau(T: np.ndarray, basis: List[int], max_iters=1000) -> Tuple[np.ndarray, List[int], List[Dict[str,Any]]]:
702
- m = T.shape[0] - 1
703
- ncols = T.shape[1]
704
- path = []
705
- path.append({'tableau': T.copy(), 'basis': basis.copy(), 'html': snapshot_html(T, basis)})
706
-
707
- it = 0
708
- while it < max_iters:
709
- it += 1
710
- obj_row = T[-1, :-1]
711
- entering_candidates = np.where(obj_row < -EPS)[0]
712
- if entering_candidates.size == 0:
713
- break
714
- entering = int(entering_candidates[0])
715
- ratios = np.full(m, np.inf)
716
- for i in range(m):
717
- a = T[i, entering]
718
- if a > EPS:
719
- ratios[i] = T[i, -1] / a
720
- if np.all(np.isinf(ratios)):
721
- raise Exception('Unbounded LP')
722
- leaving = int(np.argmin(ratios))
723
- piv = T[leaving, entering]
724
- T[leaving, :] = T[leaving, :] / piv
725
- for i in range(m+1):
726
- if i == leaving: continue
727
- T[i, :] = T[i, :] - T[i, entering] * T[leaving, :]
728
- basis[leaving] = entering
729
- path.append({'tableau': T.copy(), 'basis': basis.copy(), 'html': snapshot_html(T, basis)})
730
- return T, basis, path
731
-
732
- # ---------------- Two-Phase implementation (CORRIGIDA) ----------------
733
-
734
- def build_tableau_two_phase(c: List[float], constraints: List[Dict[str,Any]], sense: str = 'max'):
735
- obj_mult = 1.0
736
- if sense == 'min':
737
- obj_mult = -1.0
738
- c_adj = [ci * obj_mult for ci in c]
739
-
740
- n = len(c_adj)
741
- m = len(constraints)
742
-
743
- slacks = 0
744
- artificials = 0
745
- for row in constraints:
746
- if row['sense'] == '<=':
747
- slacks += 1
748
- elif row['sense'] == '>=':
749
- slacks += 1
750
- artificials += 1
751
- else:
752
- artificials += 1
753
-
754
- total_cols = n + slacks + artificials + 1
755
- T = np.zeros((m + 1, total_cols))
756
-
757
- slack_idx = n
758
- artificial_idx = n + slacks
759
-
760
- basis = []
761
- art_positions = []
762
- s_counter = 0
763
- a_counter = 0
764
-
765
- for i, row in enumerate(constraints):
766
- coeffs = row['coeffs']
767
- T[i, :n] = coeffs
768
- if row['sense'] == '<=':
769
- T[i, slack_idx + s_counter] = 1.0
770
- basis.append(slack_idx + s_counter)
771
- s_counter += 1
772
- elif row['sense'] == '>=':
773
- T[i, slack_idx + s_counter] = -1.0
774
- T[i, artificial_idx + a_counter] = 1.0
775
- basis.append(artificial_idx + a_counter)
776
- art_positions.append(artificial_idx + a_counter)
777
- s_counter += 1
778
- a_counter += 1
779
- else: # equality
780
- T[i, artificial_idx + a_counter] = 1.0
781
- basis.append(artificial_idx + a_counter)
782
- art_positions.append(artificial_idx + a_counter)
783
- a_counter += 1
784
- T[i, -1] = row['rhs']
785
-
786
- # Phase I objective: minimize sum of artificials.
787
- # Convert to maximization for our tableau solver: maximize (-sum a_j)
788
- # So c_phase1 (for maximization) = -1 for each artificial column.
789
- c_phase1 = np.zeros(total_cols - 1)
790
- for a in art_positions:
791
- c_phase1[a] = -1.0
792
-
793
- # In tableau we store -c in last row, so set T[-1, :-1] = -c_phase1
794
- T[-1, :-1] = -c_phase1
795
-
796
- # But because artificials are in basis, we must adjust objective row:
797
- # T[-1, :] = -c + sum_{i in basis} c_Bi * row_i, where c_Bi = c_phase1[basis_i]
798
- for i in range(m):
799
- bi = basis[i]
800
- cBi = c_phase1[bi] if bi < len(c_phase1) else 0.0
801
- if abs(cBi) > EPS:
802
- T[-1, :] += cBi * T[i, :]
803
-
804
- return T, basis, (n, slacks, artificials), art_positions, c_adj
805
-
806
-
807
- def run_two_phase(c, constraints, sense='max'):
808
-
809
- #Implementação 100% por tableau — compatível com HuggingFace
810
- #Phase I + Phase II completas (sem SciPy).
811
-
812
- # ---------- PHASE I ----------
813
- T0, basis0, (n_orig, n_slack, n_art), art_positions, c_adj = build_tableau_two_phase(
814
- c, constraints, sense
815
- )
816
-
817
- try:
818
- T1, basis1, path1 = primal_simplex_tableau(T0.copy(), basis0.copy())
819
- except Exception as e:
820
- return {
821
- 'status': 'phase1_failed',
822
- 'error': str(e),
823
- 'trace': traceback.format_exc()
824
- }
825
-
826
- phase1_obj = float(T1[-1, -1])
827
-
828
- # se sum(a_j) != 0 → inviável
829
- if abs(phase1_obj) > 1e-6:
830
- return {
831
- 'status': 'infeasible',
832
- 'phase1_obj': phase1_obj,
833
- 'phase1_path': path1,
834
- 'tableau_phase1': T1
835
- }
836
-
837
- # ---------- REMOVER ARTIFICIAIS ----------
838
- art_cols = set(art_positions)
839
- old_ncols = T1.shape[1] - 1
840
- keep_cols = [j for j in range(old_ncols) if j not in art_cols]
841
-
842
- # construir tableau da Phase II (T2)
843
- T2 = np.zeros((T1.shape[0], len(keep_cols) + 1))
844
- for i, col in enumerate(keep_cols):
845
- T2[:, i] = T1[:, col]
846
- T2[:, -1] = T1[:, -1]
847
-
848
- # nova base
849
- basis2 = []
850
- for bi in basis1:
851
- if bi in art_cols:
852
- basis2.append(None)
853
- else:
854
- basis2.append(keep_cols.index(bi))
855
-
856
- # corrigir linhas onde a base ficou None
857
- used = set([b for b in basis2 if b is not None])
858
- m = T2.shape[0] - 1
859
-
860
- for i in range(m):
861
- if basis2[i] is None:
862
- replaced = False
863
- for j in range(T2.shape[1] - 1):
864
- if j not in used and abs(T2[i, j]) > EPS:
865
- piv = T2[i, j]
866
- T2[i, :] = T2[i, :] / piv
867
- for r in range(m+1):
868
- if r != i:
869
- T2[r, :] -= T2[r, j] * T2[i, :]
870
- basis2[i] = j
871
- used.add(j)
872
- replaced = True
873
- break
874
- if not replaced:
875
- basis2[i] = None
876
-
877
- # ---------- PHASE II — definir objetivo original ----------
878
- c_full = []
879
- for col in keep_cols:
880
- if col < len(c_adj):
881
- c_full.append(c_adj[col])
882
- else:
883
- c_full.append(0.0)
884
-
885
- c_full = np.array(c_full)
886
-
887
- T2[-1, :-1] = -c_full
888
-
889
- for i in range(m):
890
- bi = basis2[i]
891
- if bi is not None and bi < len(c_full):
892
- coef = c_full[bi]
893
- if abs(coef) > EPS:
894
- T2[-1, :] += coef * T2[i, :]
895
-
896
- # preencher bases ausentes
897
- for i in range(m):
898
- if basis2[i] is None:
899
- for j in range(T2.shape[1]-1):
900
- if j not in used:
901
- basis2[i] = j
902
- used.add(j)
903
- break
904
-
905
- # ---------- SIMPLEX PHASE II ----------
906
- try:
907
- T_final, basis_final, path2 = primal_simplex_tableau(T2.copy(), basis2.copy())
908
- except Exception as e:
909
- return {
910
- 'status': 'phase2_failed',
911
- 'error': str(e),
912
- 'phase1_path': path1,
913
- 'trace': traceback.format_exc()
914
- }
915
-
916
- # ---------- EXTRAI X*, REDUCED COSTS E SHADOW PRICES ----------
917
- x = [0.0] * n_orig
918
-
919
- for i, bi in enumerate(basis_final):
920
- if bi is not None:
921
- oldcol = keep_cols[bi]
922
- if oldcol < n_orig:
923
- x[oldcol] = float(T_final[i, -1])
924
-
925
- z = float(T_final[-1, -1])
926
-
927
- # custos reduzidos apenas variáveis originais
928
- reduced = []
929
- for j in range(n_orig):
930
- if j in keep_cols:
931
- colpos = keep_cols.index(j)
932
- z_j = -T_final[-1, colpos]
933
- reduced.append(round(c_adj[j] - z_j, 8))
934
- else:
935
- reduced.append(0.0)
936
-
937
- # preços-sombra = coeficientes de slack na linha da função objetivo
938
- shadow = []
939
- for i in range(len(constraints)):
940
- col = n_orig + i
941
- if col in keep_cols:
942
- idx = keep_cols.index(col)
943
- shadow.append(round(-T_final[-1, idx], 8))
944
- else:
945
- shadow.append(0.0)
946
-
947
- return {
948
- 'status': 'optimal',
949
- 'x': [round(v, 8) for v in x],
950
- 'obj': round(z, 8),
951
- 'path_phase1': path1,
952
- 'path_phase2': path2,
953
- 'tableau_final': T_final,
954
- 'basis_final': basis_final,
955
- 'reduced_costs': reduced,
956
- 'shadow_prices': shadow
957
- }
958
-
959
-
960
- # ---------------- Helpers & PDF ----------------
961
-
962
- def clean_vector(vec):
963
- try:
964
- return [float(v) for v in vec]
965
- except:
966
- return vec
967
-
968
-
969
- # ---------------- Gradio handler ----------------
970
-
971
- def run_algorithms(nvars_str, objective_str, cons_str, sense, mode):
972
- try:
973
- nvars = int(nvars_str)
974
- if nvars <= 0:
975
- return 'Erro: nvars deve ser inteiro positivo', '', '', '', ''
976
- c = parse_coeffs(objective_str)
977
- if len(c) != nvars:
978
- return 'Erro: coeficientes do objetivo não correspondem a nvars', '', '', '', ''
979
- constraints, free_vars = parse_constraints(cons_str, nvars)
980
- if free_vars:
981
- nvars, c, constraints, mapping = expand_free_variables(nvars, c, constraints, free_vars)
982
- except Exception as e:
983
- return f'Erro ao ler entrada: {e}', '', '', '', ''
984
-
985
- res = run_two_phase(c, constraints, sense)
986
- status = res.get('status')
987
- # infeasible detected in Phase I
988
- if status == 'infeasible':
989
- return f"Problema inviável (Phase I obj = {res.get('phase1_obj')})", '', '', '', ''
990
-
991
- # Phase I failed
992
- if status == 'phase1_failed':
993
- return f"Erro na Phase I: {res.get('error','(sem detalhe)')}", '', '', '', ''
994
-
995
- # Phase II via tableau succeeded (our run_two_phase returns 'optimal' in that case)
996
- if status == 'optimal':
997
- x_primal = res['x']
998
- z_primal = res['obj']
999
- reduced = res.get('reduced_costs', [])
1000
- shadow = res.get('shadow_prices', [])
1001
- T_final = res.get('tableau_final', None)
1002
- path_primal = res.get('path_phase2', [])
1003
- path_phase1 = res.get('path_phase1', [])
1004
-
1005
- # Phase II solved via scipy.linprog successfully
1006
- elif status == 'optimal_linprog':
1007
- x_primal = res.get('x', [])
1008
- z_primal = res.get('obj', None)
1009
- # reduced costs / shadow prices may not be available from linprog
1010
- reduced = res.get('reduced_costs', [])
1011
- shadow = res.get('shadow_prices', [])
1012
- T_final = None
1013
- path_primal = res.get('phase1_path', []) # we still have Phase I path
1014
- path_phase1 = res.get('phase1_path', [])
1015
-
1016
- elif status in ('linprog_failed', 'no_scipy_or_error'):
1017
- msg = res.get('linprog_message') or res.get('error') or 'linprog falhou (detalhes indisponíveis)'
1018
- phase1_path = res.get('phase1_path', [])
1019
- return f"Erro na resolução: {status} - {msg}", '', '', '', ''
1020
- else:
1021
- # fallback catch-all
1022
- return f"Erro na resolução: status inesperado '{status}' - {res.get('error','')}", '', '', '', ''
1023
-
1024
- x_primal = res['x']; z_primal = res['obj']
1025
- reduced = res['reduced_costs']; shadow = res['shadow_prices']
1026
- T_final = res['tableau_final']
1027
- path2 = res['path_phase2']; path1 = res['path_phase1']
1028
-
1029
- steps_html_phase2 = ""
1030
- for idx, step in enumerate(path2):
1031
- # step is dict with 'tableau' and 'basis'
1032
- steps_html_phase2 += f"<h4>Phase II — Passo {idx+1} — Base: {step.get('basis','?')}</h4>"
1033
- steps_html_phase2 += snapshot_html(np.array(step['tableau']), step.get('basis', [])) + "<br/>"
1034
-
1035
- steps_html_phase1 = ""
1036
- for idx, step in enumerate(path1):
1037
- steps_html_phase1 += f"<h4>Phase I — Passo {idx+1} — Base: {step.get('basis','?')}</h4>"
1038
- steps_html_phase1 += snapshot_html(np.array(step['tableau']), step.get('basis', [])) + "<br/>"
1039
-
1040
- df = pd.DataFrame({'Variável': [f'x{i+1}' for i in range(len(x_primal))], 'Valor': x_primal})
1041
- solution_html = df.to_html(index=False)
1042
- solution_html += f"<p><b>Valor ótimo (estimado) = {z_primal:.6g}</b></p>"
1043
-
1044
- x_primal = clean_vector(x_primal); reduced = clean_vector(reduced); shadow = clean_vector(shadow)
1045
- z_primal = float(z_primal)
1046
-
1047
- model_txt = f"Objective ({'min' if sense=='min' else 'max'}): {c}\nConstraints:\n"
1048
- for r in constraints:
1049
- model_txt += f" {r['coeffs']} {r['sense']} {r['rhs']}\n"
1050
-
1051
- summary = ""
1052
- summary += f"Solução primal x* = {x_primal}\n"
1053
- summary += f"Z_primal (estimado) = {z_primal:.6g}\n"
1054
- summary += f"Preços-sombra (dual estimado) = {shadow}\n"
1055
- summary += f"Custos reduzidos (orig vars) = {reduced}\n"
1056
-
1057
- return model_txt, solution_html, steps_html_phase2, steps_html_phase1, summary
1058
-
1059
- # ---------------- Gradio UI ----------------
1060
-
1061
- with gr.Blocks() as demo:
1062
- gr.Markdown("# Simplex — Duas Fases (Phase I / Phase II) — Educational")
1063
- with gr.Row():
1064
- with gr.Column(scale=1):
1065
- nvars = gr.Textbox(label='Número de variáveis (n)', value='2')
1066
- objective = gr.Textbox(label='Coeficientes da função objetivo (ex: \"60 30\")', value='60 30')
1067
- cons = gr.Textbox(label='Restrições (uma por linha). Ex.: 2x1 + 3x2 <= 300', lines=6,
1068
- value='2x1 + 4x2 >= 40\n3x1 + 2x2 >= 50')
1069
- sense = gr.Radio(['max','min'], value='max', label='Tipo de objetivo')
1070
- run = gr.Button('Executar Simplex (Duas Fases)')
1071
- with gr.Column(scale=2):
1072
- model_out = gr.Textbox(label='Função objetivo e restrições (modelo)', lines=6)
1073
- solution_out = gr.HTML(label='Solução ótima (tabela)')
1074
- steps_phase2_out = gr.HTML(label='Passos do Simplex (Phase II tableaus)')
1075
- steps_phase1_out = gr.HTML(label='Passos do Simplex (Phase I tableaus)')
1076
- summary_out = gr.Textbox(label='Resumo', lines=8)
1077
-
1078
- run.click(run_algorithms, inputs=[nvars, objective, cons, sense, gr.State(value='primal_and_dual')], outputs=[model_out, solution_out, steps_phase2_out, steps_phase1_out, summary_out])
1079
-
1080
-
1081
-
1082
- gr.Examples(examples=[["2","60 30","2x1 + 4x2 >= 40\n3x1 + 2x2 >= 50","max"]], inputs=[nvars, objective, cons, sense])
1083
-
1084
- if __name__ == '__main__':
1085
- demo.launch(ssr_mode=False)"""
1086
-
1087
-
 
86
 
87
  return cons, sorted(list(set(free_vars)))
88
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
89
  def expand_free_variables(nvars: int, c: List[float], constraints: List[Dict[str,Any]], free_vars: List[int]):
90
  new_c = []
91
  mapping = {}
 
226
  return T, basis, (n, slacks, artificials), art_positions, c_adj
227
 
228
 
229
+ def run_two_phase(c, constraints, sense='max'):
230
+ #FASE I
 
 
 
 
231
  T0, basis0, (n_orig, n_slack, n_art), art_positions, c_adj = build_tableau_two_phase(
232
  c, constraints, sense
233
  )
 
257
  old_ncols = T1.shape[1] - 1
258
  keep_cols = [j for j in range(old_ncols) if j not in art_cols]
259
 
260
+ # construir tableau da fase II (T2)
261
  T2 = np.zeros((T1.shape[0], len(keep_cols) + 1))
262
  for i, col in enumerate(keep_cols):
263
  T2[:, i] = T1[:, col]
 
292
  if not replaced:
293
  basis2[i] = None
294
 
295
+ #FASE II — definir objetivo original
296
  c_full = []
297
  for col in keep_cols:
298
  if col < len(c_adj):
 
320
  used.add(j)
321
  break
322
 
323
+ #SIMPLEX FASE II
324
  try:
325
  T_final, basis_final, path2 = primal_simplex_tableau(T2.copy(), basis2.copy())
326
  except Exception as e:
 
331
  'trace': traceback.format_exc()
332
  }
333
 
334
+ #EXTRAI X*, REDUCED COSTS E DUAL (GERAL)
335
  x = [0.0] * n_orig
336
 
337
  for i, bi in enumerate(basis_final):
 
447
  status = res.get('status')
448
  # infeasible detected in Phase I
449
  if status == 'infeasible':
450
+ return f"Problema inviável (Fase I obj = {res.get('phase1_obj')})", '', '', '', ''
451
 
452
  # Phase I failed
453
  if status == 'phase1_failed':
454
+ return f"Erro na Fase I: {res.get('error','(sem detalhe)')}", '', '', '', ''
455
 
456
  if status == 'optimal':
457
  x_primal = res['x']
 
505
  # ---------------- Gradio UI ----------------
506
 
507
  with gr.Blocks() as demo:
508
+ gr.Markdown("# Simplex — Duas Fases (Fase I / Fase II) (Dual Geral)")
509
  with gr.Row():
510
  with gr.Column(scale=1):
511
  nvars = gr.Textbox(label='Número de variáveis (n)', value='2')
 
527
 
528
  if __name__ == '__main__':
529
  demo.launch(ssr_mode=False)
530
+