gamspy 1.18.1__py3-none-any.whl → 1.18.2__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
gamspy/_algebra/domain.py CHANGED
@@ -90,4 +90,4 @@ class Domain:
90
90
  -------
91
91
  str
92
92
  """
93
- return utils._get_domain_str(self.sets)[1:-1]
93
+ return utils._get_domain_str(self.sets, latex=True)[1:-1]
@@ -234,7 +234,7 @@ def create_latex_expression(root_node: Expression) -> str:
234
234
  if node.right is not None:
235
235
  s1.append(node.right)
236
236
 
237
- # 2. Build the GAMS expression
237
+ # 2. Build the LaTeX expression
238
238
  eval_stack: list[tuple[str, float]] = []
239
239
  for node in reversed(post_order_nodes):
240
240
  if not isinstance(node, Expression):
@@ -96,8 +96,15 @@ class Operation(operable.Operable):
96
96
  """
97
97
  assert self.container is not None
98
98
  temp_name = "a" + utils._get_unique_name()
99
+ domain: list[Set | Alias] = []
100
+ for elem in self.domain:
101
+ if hasattr(elem, "dimension") and elem.dimension > 1:
102
+ domain.extend(elem.domain)
103
+ else:
104
+ domain.append(elem)
105
+
99
106
  temp_param = syms.Parameter._constructor_bypass(
100
- self.container, temp_name, self.domain
107
+ self.container, temp_name, domain
101
108
  )
102
109
  temp_param[...] = self
103
110
  del self.container.data[temp_name]
@@ -756,7 +763,7 @@ class Ord(operable.Operable):
756
763
  -------
757
764
  str
758
765
  """
759
- return f"ord({self._symbol.name})"
766
+ return f"ord({self._symbol._latex_name})"
760
767
 
761
768
 
762
769
  class Card(operable.Operable):
@@ -835,4 +842,4 @@ class Card(operable.Operable):
835
842
  -------
836
843
  str
837
844
  """
838
- return f"card({self._symbol.name})"
845
+ return f"card({self._symbol._latex_name})"
gamspy/_container.py CHANGED
@@ -1099,7 +1099,7 @@ class Container(gt.Container):
1099
1099
  self,
1100
1100
  name: str | None = None,
1101
1101
  type: str | EquationType = "regular",
1102
- domain: Sequence[Set | Alias | str] | Set | Alias | str | None = None,
1102
+ domain: Sequence[Set | Alias] | Set | Alias | None = None,
1103
1103
  definition: Variable | Operation | Expression | None = None,
1104
1104
  records: Any | None = None,
1105
1105
  domain_forwarding: bool | list[bool] = False,
@@ -1117,7 +1117,7 @@ class Container(gt.Container):
1117
1117
  Name of the equation. Name is autogenerated by default.
1118
1118
  type : str
1119
1119
  Type of the equation. "regular" by default.
1120
- domain : Sequence[Set | Alias | str] | Set | Alias | str, optional
1120
+ domain : Sequence[Set | Alias] | Set | Alias, optional
1121
1121
  Domain of the variable.
1122
1122
  definition: Expression, optional
1123
1123
  Definition of the equation.
gamspy/_convert.py CHANGED
@@ -1,6 +1,5 @@
1
1
  from __future__ import annotations
2
2
 
3
- import logging
4
3
  import os
5
4
  import subprocess
6
5
  from collections.abc import Sequence
@@ -28,14 +27,6 @@ if TYPE_CHECKING:
28
27
 
29
28
  SymbolType: TypeAlias = Alias | Set | Parameter | Variable | Equation
30
29
 
31
- logger = logging.getLogger("CONVERTER")
32
- logger.setLevel(logging.INFO)
33
- stream_handler = logging.StreamHandler()
34
- stream_handler.setLevel(logging.INFO)
35
- formatter = logging.Formatter("[%(name)s - %(levelname)s] %(message)s")
36
- stream_handler.setFormatter(formatter)
37
- logger.addHandler(stream_handler)
38
-
39
30
  GAMS_JACOBIAN_TEMPLATE = """$onEmpty
40
31
  Set
41
32
  i 'equations index'
@@ -388,9 +379,9 @@ class GamsConverter:
388
379
  self.path = path
389
380
  self.options = options
390
381
  self.dump_gams_state = dump_gams_state
391
- self.gdx_path = str((path / f"{model.name}_data.gdx").resolve())
392
- self.gms_path = str((path / f"{model.name}.gms").resolve())
393
- self.g00_path = str((path / f"{model.name}.g00").resolve())
382
+ self.gdx_path = path / f"{model.name}_data.gdx"
383
+ self.gms_path = path / f"{model.name}.gms"
384
+ self.g00_path = path / f"{model.name}.g00"
394
385
 
395
386
  def get_definitions(self) -> list[str]:
396
387
  definitions = []
@@ -438,8 +429,8 @@ class GamsConverter:
438
429
  em_name = self.model._external_module
439
430
  em_file = self.model._external_module_file
440
431
  declarations.append(f"File {em_file} /{em_name}/;")
441
- logger.info("Converter will not copy external module files")
442
- logger.info(
432
+ print("Converter will not copy external module files")
433
+ print(
443
434
  f"You need to ensure your external module is accessible from {self.path}"
444
435
  )
445
436
 
@@ -482,7 +473,9 @@ class GamsConverter:
482
473
  with open(self.gms_path, "w", encoding="utf-8") as file:
483
474
  file.write(gams_string)
484
475
 
485
- logger.info(f"GAMS (.gms) file has been generated under {self.gms_path}")
476
+ print("=" * 80)
477
+ print(f"GAMS (.gms) file has been generated under {self.gms_path}")
478
+ print("=" * 80)
486
479
 
487
480
 
488
481
  TABLE_HEADER = """\\begin{tabularx}{\\textwidth}{| l | l | X |}
@@ -496,7 +489,7 @@ TABLE_FOOTER = "\\hline\n\\end{tabularx}"
496
489
 
497
490
 
498
491
  class LatexConverter:
499
- def __init__(self, model: Model, path: Path) -> None:
492
+ def __init__(self, model: Model, path: Path, rename: dict[str, str] | None) -> None:
500
493
  os.makedirs(path, exist_ok=True)
501
494
  self.model = model
502
495
  self.container = model.container
@@ -512,11 +505,21 @@ class LatexConverter:
512
505
  ):
513
506
  symbols.append(elem.name)
514
507
 
508
+ for name in self.model._autogen_symbols:
509
+ if name in symbols:
510
+ symbols.remove(name)
511
+
515
512
  self.symbols = sorted(symbols, key=list(self.container.data.keys()).index)
516
513
 
517
- for name in self.model._autogen_symbols:
518
- if name in self.symbols:
519
- self.symbols.remove(name)
514
+ if rename is not None:
515
+ for name, latex_name in rename.items():
516
+ try:
517
+ symbol = self.container[name]
518
+ symbol._latex_name = latex_name
519
+ except KeyError as e:
520
+ raise KeyError(
521
+ f"`{name}` does not exist in the Container. You can only rename symbols that already exist in the container."
522
+ ) from e
520
523
 
521
524
  self.header = self.get_header()
522
525
  self.set_header = "\\subsection*{Sets}"
@@ -551,8 +554,9 @@ class LatexConverter:
551
554
  if self.model._objective is None:
552
555
  ...
553
556
  elif isinstance(self.model._objective, syms.Variable):
557
+ var_name = self.model._objective._latex_name
554
558
  latex_strs.append(
555
- f"\\textbf{{{str(self.model.sense).lower()}}} ${self.model._objective_variable.name}$\\\\" # type: ignore
559
+ f"\\textbf{{{str(self.model.sense).lower()}}} ${var_name}$\\\\"
556
560
  )
557
561
  latex_strs.append("\\textbf{s.t.}")
558
562
  else:
@@ -572,15 +576,18 @@ class LatexConverter:
572
576
  with open(self.tex_path, "w", encoding="utf-8") as file: # Write the TEX file
573
577
  file.write(latex_str)
574
578
 
575
- logger.info(
579
+ print("=" * 80)
580
+ print(
576
581
  f"LaTeX (.tex) file has been generated under {os.path.join(self.path, self.model.name + '.tex')}"
577
582
  )
583
+ print("=" * 80)
578
584
 
579
585
  self.latex_str = latex_str
580
586
 
581
587
  def to_pdf(self) -> None:
582
- process = subprocess.run(["pdflatex", "-v"])
588
+ process = subprocess.run(["pdflatex", "-v"], capture_output=True)
583
589
  if process.returncode:
590
+ print(process.stderr)
584
591
  raise ValidationError(
585
592
  "`pdflatex` is required to generate the pdf! Please install `pdflatex` and add it to the path."
586
593
  )
@@ -604,10 +611,11 @@ class LatexConverter:
604
611
  if isinstance(symbol, syms.Variable) and self.model._limited_variables:
605
612
  for elem in self.model._limited_variables:
606
613
  if elem.name == symbol.name:
607
- domain_str = utils._get_domain_str(elem.domain)[1:-1]
614
+ domain_str = utils._get_domain_str(elem.domain, latex=True)[
615
+ 1:-1
616
+ ]
608
617
 
609
- row = f"{summary['name']} & {domain_str} & {summary['description']}\\\\"
610
- row = row.replace("_", "\\_")
618
+ row = f"{symbol._latex_name} & {domain_str} & {summary['description']}\\\\"
611
619
  table.append(row)
612
620
 
613
621
  table.append(TABLE_FOOTER)
@@ -615,13 +623,13 @@ class LatexConverter:
615
623
  return "\n".join(table)
616
624
 
617
625
  def get_definitions(self) -> str:
618
- definitions = []
626
+ definitions: list[str] = []
619
627
  for equation in self.model.equations:
620
628
  if equation.name in self.model._autogen_symbols:
621
629
  continue
622
630
 
623
- domain_str = ",".join([elem.name for elem in equation.domain])
624
- header = "\\subsubsection*{$" + equation.name.replace("_", "\\_")
631
+ domain_str = ",".join([elem.latexRepr() for elem in equation.domain])
632
+ header = "\\subsubsection*{$" + equation._latex_name
625
633
  if domain_str:
626
634
  header += f"_{{{domain_str}}}"
627
635
  header += "$}\n"
@@ -641,20 +649,33 @@ class LatexConverter:
641
649
 
642
650
  constraint = "$" + symbol.latexRepr()
643
651
  if symbol.type == "binary":
644
- constraint += (
645
- "\\in "
646
- + r"\{0,1\}"
647
- + " ~ \\forall "
648
- + ",".join(symbol.domain_names)
649
- )
652
+ constraint += "\\in " + r"\{0,1\}"
653
+ if symbol.domain:
654
+ constraint += (
655
+ " ~ \\forall "
656
+ + utils._get_domain_str(symbol.domain, latex=True)[1:-1]
657
+ )
650
658
  elif symbol.type == "integer":
651
- constraint += "\\in \\mathbb{Z}_{+} \\forall " + ",".join(
652
- symbol.domain_names
653
- )
659
+ constraint += "\\in \\mathbb{Z}_{+}"
660
+ if symbol.domain:
661
+ constraint += (
662
+ " ~ \\forall "
663
+ + utils._get_domain_str(symbol.domain, latex=True)[1:-1]
664
+ )
654
665
  elif symbol.type == "positive":
655
- constraint += "\\geq 0 ~ \\forall " + ",".join(symbol.domain_names)
666
+ constraint += "\\geq 0"
667
+ if symbol.domain:
668
+ constraint += (
669
+ " ~ \\forall "
670
+ + utils._get_domain_str(symbol.domain, latex=True)[1:-1]
671
+ )
656
672
  elif symbol.type == "negative":
657
- constraint += "\\leq 0 ~ \\forall " + ",".join(symbol.domain_names)
673
+ constraint += "\\leq 0"
674
+ if symbol.domain:
675
+ constraint += (
676
+ " ~ \\forall "
677
+ + utils._get_domain_str(symbol.domain, latex=True)[1:-1]
678
+ )
658
679
  elif symbol.type == "sos1":
659
680
  constraint += "SOS1"
660
681
  elif symbol.type == "sos2":
@@ -672,18 +693,21 @@ class LatexConverter:
672
693
  return "\n".join(constraints)
673
694
 
674
695
  def get_header(self) -> str:
675
- header = """\\documentclass[11pt]{article}
676
- \\usepackage{geometry}
677
- \\usepackage[american]{babel}
678
- \\usepackage{amsmath}
679
- \\usepackage{amssymb}
680
- \\usepackage[hidelinks]{hyperref}
681
- \\usepackage{tabularx}
682
- \\usepackage{ltablex}
683
- \\keepXColumns
684
-
685
- \\begin{document}
686
- \\section*{Symbols}
696
+ header = r"""\documentclass[11pt]{article}
697
+ \usepackage{geometry}
698
+ \usepackage[american]{babel}
699
+ \usepackage{amsmath}
700
+ \usepackage{amssymb}
701
+ \usepackage{fontspec}
702
+ \setmainfont{CMU Serif} % 1. Sets a font that knows Greek in text mode
703
+ \usepackage{unicode-math} % 2. Allows Greek keys to work in math mode
704
+ \usepackage[hidelinks]{hyperref}
705
+ \usepackage{tabularx}
706
+ \usepackage{ltablex}
707
+ \keepXColumns
708
+
709
+ \begin{document}
710
+ \section*{Symbols}
687
711
 
688
712
  """
689
713
  return header
gamspy/_extrinsic.py CHANGED
@@ -120,7 +120,23 @@ class ExtrinsicFunction(operable.Operable):
120
120
  -------
121
121
  str
122
122
  """
123
- return self.gamsRepr()
123
+ representation = self.name.replace("_", r"\_")
124
+
125
+ if self.args:
126
+ arg_strs = []
127
+ for arg in self.args:
128
+ arg_str = (
129
+ str(arg).replace("_", r"\_")
130
+ if isinstance(arg, (int, float, str))
131
+ else arg.latexRepr()
132
+ )
133
+ arg_strs.append(arg_str)
134
+
135
+ args = ",".join(arg_strs)
136
+
137
+ representation = f"{representation}({args})"
138
+
139
+ return representation
124
140
 
125
141
 
126
142
  class ExtrinsicLibrary:
gamspy/_model.py CHANGED
@@ -396,7 +396,7 @@ class Model:
396
396
 
397
397
  # Prefix for auto-generated symbols
398
398
  _generate_prefix = "autogenerated_"
399
- _autogen_symbols: ClassVar[set[Variable | Equation]] = set()
399
+ _autogen_symbols: ClassVar[set[str]] = set()
400
400
 
401
401
  def __init__(
402
402
  self,
@@ -1213,8 +1213,8 @@ class Model:
1213
1213
  >>> v = gp.Variable(m, "v")
1214
1214
  >>> e = gp.Equation(m, "e", definition= v == 5)
1215
1215
  >>> my_model = gp.Model(m, "my_model", problem="LP", equations=[e])
1216
- >>> my_model.convert("output_directory", gp.FileFormat.GAMS)
1217
- >>> my_model.convert("output_directory", [gp.FileFormat.GAMS, gp.FileFormat.AMPL])
1216
+ >>> my_model.convert("tmp", gp.FileFormat.GAMS)
1217
+ >>> my_model.convert("tmp", [gp.FileFormat.GAMS, gp.FileFormat.AMPL])
1218
1218
 
1219
1219
  """
1220
1220
  path = Path(path)
@@ -1553,6 +1553,19 @@ class Model:
1553
1553
  ------
1554
1554
  ValidationError
1555
1555
  In case the given options is not of type gp.Options.
1556
+
1557
+ Examples
1558
+ --------
1559
+ >>> import gamspy as gp
1560
+ >>> m = gp.Container()
1561
+ >>> v = gp.Variable(m, "v")
1562
+ >>> e = gp.Equation(m, "e", definition= v == 5)
1563
+ >>> my_model = gp.Model(m, "my_model", problem="LP", equations=[e])
1564
+ >>> my_model.toGams("tmp") # doctest: +ELLIPSIS
1565
+ ================================================================================
1566
+ GAMS (.gms) file has been generated under ...
1567
+ ================================================================================
1568
+
1556
1569
  """
1557
1570
  if options is not None and not isinstance(options, gp.Options):
1558
1571
  raise ValidationError(
@@ -1563,7 +1576,13 @@ class Model:
1563
1576
  converter = GamsConverter(self, path, options, dump_gams_state)
1564
1577
  converter.convert()
1565
1578
 
1566
- def toLatex(self, path: str | Path, generate_pdf: bool = False) -> None:
1579
+ def toLatex(
1580
+ self,
1581
+ path: str | Path,
1582
+ rename: dict[str, str] | None = None,
1583
+ *,
1584
+ generate_pdf: bool = False,
1585
+ ) -> None:
1567
1586
  """
1568
1587
  Generates a latex file that contains the model definition under path/<model_name>.tex
1569
1588
 
@@ -1571,9 +1590,27 @@ class Model:
1571
1590
  ----------
1572
1591
  path : str | Path
1573
1592
  Path to the directory which will contain the .tex file.
1593
+ rename: dict[str, str], optional
1594
+ A dictionary to rename symbols in the LaTeX file. Keys are GAMSPy symbol names
1595
+ and values are the names that will be used in the LaTeX file.
1596
+ generate_pdf: bool, False by default
1597
+ Generates a pdf file if it is set. Requires pdflatex to be installed.
1598
+
1599
+ Examples
1600
+ --------
1601
+ >>> import gamspy as gp
1602
+ >>> m = gp.Container()
1603
+ >>> v = gp.Variable(m, "v")
1604
+ >>> e = gp.Equation(m, "e", definition= v == 5)
1605
+ >>> my_model = gp.Model(m, "my_model", problem="LP", equations=[e])
1606
+ >>> my_model.toLatex("tmp") # doctest: +ELLIPSIS
1607
+ ================================================================================
1608
+ LaTeX (.tex) file has been generated under ...
1609
+ ================================================================================
1610
+
1574
1611
  """
1575
1612
  path = Path(path)
1576
- converter = LatexConverter(self, path)
1613
+ converter = LatexConverter(self, path, rename)
1577
1614
  converter.convert()
1578
1615
 
1579
1616
  if generate_pdf:
gamspy/_symbols/alias.py CHANGED
@@ -72,6 +72,7 @@ class Alias(gt.Alias, operable.Operable, Symbol, SetMixin):
72
72
 
73
73
  # gamspy attributes
74
74
  obj.where = condition.Condition(obj)
75
+ obj._latex_name = name.replace("_", r"\_")
75
76
  obj.container._add_statement(obj)
76
77
  obj._metadata = {}
77
78
 
@@ -145,6 +146,7 @@ class Alias(gt.Alias, operable.Operable, Symbol, SetMixin):
145
146
  name = container._get_symbol_name(prefix="a")
146
147
 
147
148
  super().__init__(container, name, alias_with)
149
+ self._latex_name = self.name.replace("_", r"\_")
148
150
 
149
151
  validation.validate_container(self, self.domain)
150
152
  self.where = condition.Condition(self)
@@ -160,6 +160,7 @@ class Equation(gt.Equation, Symbol):
160
160
  # gamspy attributes
161
161
  obj._definition = None
162
162
  obj.where = condition.Condition(obj)
163
+ obj._latex_name = name.replace("_", r"\_")
163
164
  obj.container._add_statement(obj)
164
165
  obj._synchronize = True
165
166
  obj._metadata = {}
@@ -225,7 +226,7 @@ class Equation(gt.Equation, Symbol):
225
226
  container: Container | None = None,
226
227
  name: str | None = None,
227
228
  type: str | EquationType = "regular",
228
- domain: Sequence[Set | Alias | str] | Set | Alias | str | None = None,
229
+ domain: Sequence[Set | Alias] | Set | Alias | None = None,
229
230
  definition: Variable | Operation | Expression | None = None,
230
231
  records: Any | None = None,
231
232
  domain_forwarding: bool | list[bool] = False,
@@ -250,7 +251,7 @@ class Equation(gt.Equation, Symbol):
250
251
  if domain is None:
251
252
  domain = []
252
253
 
253
- if isinstance(domain, (gp.Set, gp.Alias, str)):
254
+ if isinstance(domain, (gp.Set, gp.Alias)):
254
255
  domain = [domain]
255
256
 
256
257
  if isinstance(domain, gp.math.Dim):
@@ -330,6 +331,7 @@ class Equation(gt.Equation, Symbol):
330
331
  description=description,
331
332
  uels_on_axes=uels_on_axes,
332
333
  )
334
+ self._latex_name = self.name.replace("_", r"\_")
333
335
 
334
336
  if is_miro_output:
335
337
  container._miro_output_symbols.append(self.name)
@@ -1143,6 +1145,8 @@ class Equation(gt.Equation, Symbol):
1143
1145
  "Equation must be defined to get its latex representation."
1144
1146
  )
1145
1147
 
1148
+ # The LHS of an equation definition can either be an ImplicitEquation or a condition.
1149
+ # e.g. e[i] = ... or e[i].where[b[i]] = ...
1146
1150
  assert isinstance(
1147
1151
  self._definition.left,
1148
1152
  (implicits.ImplicitEquation, condition.Condition),
@@ -1152,21 +1156,25 @@ class Equation(gt.Equation, Symbol):
1152
1156
  if isinstance(self._definition.left, implicits.ImplicitEquation):
1153
1157
  if len(self._definition.left.domain) > 0:
1154
1158
  domain_str = ",".join(
1155
- [symbol.name for symbol in self._definition.left.domain]
1159
+ [symbol.latexRepr() for symbol in self._definition.left.domain]
1156
1160
  )
1157
1161
  right_side = f"\\hfill \\forall {domain_str}"
1158
1162
  else:
1159
1163
  domain_str = ",".join(
1160
1164
  [
1161
- symbol.name
1165
+ symbol.latexRepr()
1162
1166
  for symbol in self._definition.left.conditioning_on.domain # type: ignore
1163
1167
  ]
1164
1168
  )
1165
1169
  domain_str = f"\\forall {domain_str}"
1166
1170
 
1167
- constraint_str = str(self._definition.left.condition)
1171
+ assert self._definition.left.condition is not None
1168
1172
  if hasattr(self._definition.left.condition, "latexRepr"):
1169
- constraint_str = self._definition.left.condition.latexRepr() # type: ignore
1173
+ constraint_str = self._definition.left.condition.latexRepr()
1174
+ else:
1175
+ assert isinstance(self._definition.left.condition, (int, float))
1176
+ constraint_str = str(self._definition.left.condition)
1177
+
1170
1178
  right_side = f"\\hfill {domain_str} ~ | ~ {constraint_str}"
1171
1179
 
1172
1180
  assert self._definition.right is not None
@@ -83,7 +83,7 @@ class ImplicitSet(ImplicitSymbol, operable.Operable):
83
83
  return temp_param.records
84
84
 
85
85
  def latexRepr(self):
86
- name = self.name.replace("_", "\\_")
86
+ name = self._latex_name
87
87
  representation = name
88
88
 
89
89
  if self.extension is not None:
@@ -100,6 +100,7 @@ class ImplicitSet(ImplicitSymbol, operable.Operable):
100
100
  if isinstance(elem, (syms.Set, syms.Alias, ImplicitSet)):
101
101
  set_strs.append(elem.latexRepr())
102
102
  elif isinstance(elem, str):
103
+ elem = elem.replace("_", r"\_")
103
104
  set_strs.append(f"\\textquotesingle {elem} \\textquotesingle")
104
105
 
105
106
  domain_str = "{" + ",".join(set_strs) + "}"
@@ -46,6 +46,10 @@ class ImplicitSymbol(ABC):
46
46
 
47
47
  self.fix_domain_scalars(parent_scalar_domains)
48
48
 
49
+ @property
50
+ def _latex_name(self) -> str:
51
+ return self.parent._latex_name
52
+
49
53
  def __bool__(self):
50
54
  raise ValidationError("A symbol cannot be used as a truth value.")
51
55
 
@@ -101,7 +105,7 @@ class ImplicitSymbol(ABC):
101
105
  def latexRepr(self):
102
106
  from .implicit_set import ImplicitSet
103
107
 
104
- name = self.name.replace("_", "\\_")
108
+ name = self._latex_name
105
109
  representation = name
106
110
  domain = list(self.domain)
107
111
 
@@ -114,6 +118,7 @@ class ImplicitSymbol(ABC):
114
118
  if isinstance(elem, (gp.Set, gp.Alias, ImplicitSet)):
115
119
  set_strs.append(elem.latexRepr())
116
120
  elif isinstance(elem, str):
121
+ elem = elem.replace("_", r"\_")
117
122
  set_strs.append(f"\\textquotesingle {elem} \\textquotesingle")
118
123
 
119
124
  domain_str = "{" + ",".join(set_strs) + "}"
@@ -113,6 +113,7 @@ class Parameter(gt.Parameter, operable.Operable, Symbol):
113
113
  # gamspy attributes
114
114
  obj._synchronize = True
115
115
  obj.where = condition.Condition(obj)
116
+ obj._latex_name = name.replace("_", r"\_")
116
117
  obj.container._add_statement(obj)
117
118
  obj._metadata = {}
118
119
  obj._winner = "python"
@@ -263,6 +264,7 @@ class Parameter(gt.Parameter, operable.Operable, Symbol):
263
264
  description=description,
264
265
  uels_on_axes=uels_on_axes,
265
266
  )
267
+ self._latex_name = self.name.replace("_", r"\_")
266
268
 
267
269
  if is_miro_input:
268
270
  self._already_loaded = False
gamspy/_symbols/set.py CHANGED
@@ -482,6 +482,7 @@ class Set(gt.Set, operable.Operable, Symbol, SetMixin):
482
482
 
483
483
  # gamspy attributes
484
484
  obj.where = condition.Condition(obj)
485
+ obj._latex_name = name.replace("_", r"\_")
485
486
  obj.container._add_statement(obj)
486
487
  obj._synchronize = True
487
488
  obj._metadata = {}
@@ -642,6 +643,7 @@ class Set(gt.Set, operable.Operable, Symbol, SetMixin):
642
643
  description=description,
643
644
  uels_on_axes=uels_on_axes,
644
645
  )
646
+ self._latex_name = self.name.replace("_", r"\_")
645
647
 
646
648
  if is_miro_input:
647
649
  self._already_loaded = False
gamspy/_symbols/symbol.py CHANGED
@@ -47,8 +47,7 @@ class Symbol:
47
47
  -------
48
48
  str
49
49
  """
50
- name = self.name.replace("_", "\\_")
51
- return name
50
+ return self._latex_name
52
51
 
53
52
  @property
54
53
  def synchronize(
@@ -63,6 +63,7 @@ class UniverseAlias(gt.UniverseAlias):
63
63
 
64
64
  # gamspy attributes
65
65
  obj.where = condition.Condition(obj)
66
+ obj._latex_name = name.replace("_", r"\_")
66
67
 
67
68
  # add statement
68
69
  obj.container._add_statement(obj)
@@ -103,6 +104,7 @@ class UniverseAlias(gt.UniverseAlias):
103
104
  raise ValidationError("UniverseAlias requires a container.") from e
104
105
 
105
106
  super().__init__(container, name)
107
+ self._latex_name = self.name.replace("_", r"\_")
106
108
 
107
109
  # allow conditions
108
110
  self.where = condition.Condition(self)
@@ -137,6 +139,25 @@ class UniverseAlias(gt.UniverseAlias):
137
139
  """
138
140
  return self.name
139
141
 
142
+ def latexRepr(self) -> str:
143
+ """
144
+ Representation of the UniverseAlias in LaTeX.
145
+
146
+ Returns
147
+ -------
148
+ str
149
+
150
+ Examples
151
+ --------
152
+ >>> import gamspy as gp
153
+ >>> m = gp.Container()
154
+ >>> i = gp.UniverseAlias(m, name="universe_alias")
155
+ >>> i.latexRepr()
156
+ 'universe\\_alias'
157
+
158
+ """
159
+ return self._latex_name
160
+
140
161
  def getDeclaration(self) -> str:
141
162
  """
142
163
  Declaration of the UniverseAlias in GAMS
@@ -155,6 +155,7 @@ class Variable(gt.Variable, operable.Operable, Symbol):
155
155
 
156
156
  # gamspy attributes
157
157
  obj.where = condition.Condition(obj)
158
+ obj._latex_name = name.replace("_", r"\_")
158
159
  obj.container._add_statement(obj)
159
160
  obj._synchronize = True
160
161
  obj._metadata = {}
@@ -319,6 +320,7 @@ class Variable(gt.Variable, operable.Operable, Symbol):
319
320
  description=description,
320
321
  uels_on_axes=uels_on_axes,
321
322
  )
323
+ self._latex_name = self.name.replace("_", r"\_")
322
324
 
323
325
  if is_miro_output:
324
326
  container._miro_output_symbols.append(self.name)
gamspy/_validation.py CHANGED
@@ -187,7 +187,7 @@ def validate_dimension(
187
187
 
188
188
  if dimension != symbol.dimension:
189
189
  raise ValidationError(
190
- f"The `{symbol}` is referenced with"
190
+ f"The `{symbol.gamsRepr()}` is referenced with"
191
191
  f" {'more' if dimension > symbol.dimension else 'less'} indices"
192
192
  f" than declared. Declared dimension is {symbol.dimension} but"
193
193
  f" given dimension is {dimension}"
gamspy/math/misc.py CHANGED
@@ -8,6 +8,7 @@ import gamspy._algebra.operable as operable
8
8
  import gamspy._symbols as syms
9
9
  import gamspy._validation as validation
10
10
  import gamspy.utils as utils
11
+ from gamspy._container import Container
11
12
  from gamspy.exceptions import ValidationError
12
13
 
13
14
  if TYPE_CHECKING:
@@ -55,13 +56,16 @@ class MathOp(operable.Operable):
55
56
  -------
56
57
  pd.DataFrame | None
57
58
  """
58
- assert self.container is not None
59
+ container = self.container
60
+ if container is None:
61
+ container = Container()
62
+
59
63
  temp_name = "a" + utils._get_unique_name()
60
64
  temp_param = syms.Parameter._constructor_bypass(
61
- self.container, temp_name, self.domain
65
+ container, temp_name, self.domain
62
66
  )
63
67
  temp_param[...] = self
64
- del self.container.data[temp_name]
68
+ del container.data[temp_name]
65
69
  return temp_param.records
66
70
 
67
71
  def toValue(self) -> float | None:
@@ -121,11 +125,14 @@ class MathOp(operable.Operable):
121
125
  "abs": "\\lvert",
122
126
  }
123
127
 
124
- operands_str = ",".join([_stringify(elem) for elem in self.elements])
128
+ operands_str = ",".join(
129
+ [_stringify(elem, latex=True) for elem in self.elements]
130
+ )
125
131
  if self.op_name in op_map:
126
132
  return f"{op_map[self.op_name]}{{{operands_str}}}"
127
133
 
128
- return f"{self.op_name}({operands_str})"
134
+ op_name = self.op_name.replace("_", r"\_")
135
+ return f"{op_name}({operands_str})"
129
136
 
130
137
  def __str__(self):
131
138
  return self.gamsRepr()
@@ -134,14 +141,20 @@ class MathOp(operable.Operable):
134
141
  return len(self.gamsRepr())
135
142
 
136
143
 
137
- def _stringify(x: str | int | float | Symbol | ImplicitSymbol):
144
+ def _stringify(x: str | int | float | Symbol | ImplicitSymbol, *, latex: bool = False):
138
145
  if isinstance(x, (int, float)):
139
146
  x = utils._map_special_values(x)
140
147
 
141
148
  return str(x)
142
149
  elif isinstance(x, str):
150
+ if latex:
151
+ x = x.replace("_", r"\_")
152
+
143
153
  return f'"{x}"'
144
154
 
155
+ if latex:
156
+ return x.latexRepr()
157
+
145
158
  return x.gamsRepr() # type: ignore
146
159
 
147
160
 
gamspy/utils.py CHANGED
@@ -584,7 +584,9 @@ def _map_special_values(value: float):
584
584
  return value
585
585
 
586
586
 
587
- def _get_domain_str(domain: Iterable[Set | Alias | ImplicitSet | str]) -> str:
587
+ def _get_domain_str(
588
+ domain: Iterable[Set | Alias | ImplicitSet | str], *, latex: bool = False
589
+ ) -> str:
588
590
  """
589
591
  Creates the string format of a given domain
590
592
 
@@ -601,21 +603,29 @@ def _get_domain_str(domain: Iterable[Set | Alias | ImplicitSet | str]) -> str:
601
603
  Exception
602
604
  Given domain must contain only sets, aliases or strings
603
605
  """
604
- set_strs = []
605
- for set in domain:
606
- if isinstance(set, (gt.Set, gt.Alias, gt.UniverseAlias, implicits.ImplicitSet)):
607
- set_strs.append(set.gamsRepr())
608
- elif isinstance(set, str):
609
- if set == "*":
610
- set_strs.append(set)
606
+ domain_strs = []
607
+ for elem in domain:
608
+ if isinstance(
609
+ elem, (gt.Set, gt.Alias, gt.UniverseAlias, implicits.ImplicitSet)
610
+ ):
611
+ if latex:
612
+ domain_strs.append(elem.latexRepr())
613
+ else:
614
+ domain_strs.append(elem.gamsRepr())
615
+ elif isinstance(elem, str):
616
+ if elem == "*":
617
+ domain_strs.append(elem)
611
618
  else:
612
- set_strs.append('"' + set + '"')
619
+ if latex:
620
+ domain_strs.append('"' + elem.replace("_", r"\_") + '"')
621
+ else:
622
+ domain_strs.append('"' + elem + '"')
613
623
  else:
614
624
  raise ValidationError(
615
- f"Domain type must be str, Set or Alias but found {type(set)}"
625
+ f"Domain type must be str, Set or Alias but found {type(elem)}"
616
626
  )
617
627
 
618
- return "(" + ",".join(set_strs) + ")"
628
+ return "(" + ",".join(domain_strs) + ")"
619
629
 
620
630
 
621
631
  def _permute_domain(domain, dims):
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: gamspy
3
- Version: 1.18.1
3
+ Version: 1.18.2
4
4
  Summary: Python-based algebraic modeling interface to GAMS
5
5
  Author-email: GAMS Development Corporation <support@gams.com>
6
6
  Project-URL: homepage, https://gams.com/sales/gamspy_facts/
@@ -31,8 +31,8 @@ Classifier: Operating System :: Microsoft :: Windows
31
31
  Requires-Python: >=3.10
32
32
  Description-Content-Type: text/markdown
33
33
  License-File: LICENSE
34
- Requires-Dist: gamsapi<53.0.0,>=52.1.0
35
- Requires-Dist: gamspy_base<53.0.0,>=52.1.0
34
+ Requires-Dist: gamsapi<53.0.0,>=52.2.0
35
+ Requires-Dist: gamspy_base<53.0.0,>=52.2.0
36
36
  Requires-Dist: pandas<2.4,>=2.2.2
37
37
  Requires-Dist: pydantic>=2.0
38
38
  Requires-Dist: requests>=2.28.0
@@ -2,29 +2,29 @@ gamspy/__init__.py,sha256=HZulKBgt2ubfX6r6v6Xv3MJVI6Prm40j5W45aUetQok,1797
2
2
  gamspy/__main__.py,sha256=3HW3NYEe4pZnGhJ10s1G0vaZAkI9Zl_lXqHT9iDNXQg,108
3
3
  gamspy/_communication.py,sha256=18drDsKxi1Iv6oAKZfKGJcPYYWebBQsyeOxiEKXZngE,7148
4
4
  gamspy/_config.py,sha256=n7RXUFczIlol15-mxhnj54fHXHet8HcOnWiGm3mpMgg,3589
5
- gamspy/_container.py,sha256=w5zftBCrgFSkZEOgwh2zyFjiDA4AohzNHi-BuF0Hjas,45884
6
- gamspy/_convert.py,sha256=z_ju0T2duAAeYE0ahW7E2GMdMMVZsQ5936HV583hXzs,22294
5
+ gamspy/_container.py,sha256=meoK0UFi-8oZOn9A1l5rKrZWUUIGREaBM04aWvg6el8,45860
6
+ gamspy/_convert.py,sha256=g8rtrSwKkETWYvtp1loxsJ2uuIPrS1L6VP1EMypJghY,23259
7
7
  gamspy/_database.py,sha256=SVo4lEfS0Q2f80uMu7k71BBQNRwUei-2KFc_EGxsOcw,6873
8
- gamspy/_extrinsic.py,sha256=6pBam_ruzKaYjEpALjAQ5d9uPrlOf1lVMYjRYqHH8o0,4337
8
+ gamspy/_extrinsic.py,sha256=OGrlNzcSQjoIZw8lpsk_4SObEAhGX5bkc_D0d3lPZMQ,4807
9
9
  gamspy/_miro.py,sha256=MeFGcaFtNgtydWjH0KS0l9y3mNy7tPyXwm43jwomodg,12245
10
- gamspy/_model.py,sha256=Qd8JdDDUUSjWXQUKGR4N0-2J_PHnzh8sjSG3U7A4aKk,51359
10
+ gamspy/_model.py,sha256=fcHa3W8ioTUBI6qOQsPk-HRU9i9PEKhrI-grlK4Uvzc,52860
11
11
  gamspy/_model_instance.py,sha256=ZHG3-inc6vDx7F2Oo9HYWYAi8hki6upIgAQn5II_o1U,24384
12
12
  gamspy/_options.py,sha256=FTty65WRdikvnhPyWSTuwuUz0Rs6aZwDFWL-UsfEgd0,27211
13
13
  gamspy/_serialization.py,sha256=fwAQpABCZQyxtAkddsvCDjtyQyPUT3EJy19r9BKR6aY,4062
14
14
  gamspy/_types.py,sha256=_KQjRntvIAicc9_sZ5BYWAHjs-WBURf2NJwTpJQT5xw,162
15
- gamspy/_validation.py,sha256=k4e8M8X32hEFybJyISyXLNrzO1mmWXQPnSThT7rOC5U,21608
15
+ gamspy/_validation.py,sha256=Wa5Rv0-QY-ij1RiNIOtuIplWXCtHjQDNvSVqNdu0V10,21619
16
16
  gamspy/_workspace.py,sha256=QrnC8aPN5C9zyqbaYvfuPlRHvBrDx_i4Imw1AzsJs7k,2175
17
17
  gamspy/exceptions.py,sha256=q-Cb6syXv1abf2JBfNFidGkvOWkp9FXU5NzJwkR5W7o,2929
18
18
  gamspy/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
19
- gamspy/utils.py,sha256=6eO4R0iBLRm2oTRCz_croiQhKpiIyF8bOa0NDfw9aUU,19777
19
+ gamspy/utils.py,sha256=Am8Zr40GtLwIp1waRps3HElpzLwPzKBWVZi05PAacEY,20078
20
20
  gamspy/version.py,sha256=GsqTw9rQ4Zz80ZSlIo44vYqgcVBJSbGOqJFJW3j_z88,109
21
21
  gamspy/_algebra/__init__.py,sha256=HdTHRBzfOGXihibQOAOAHGHKinlMuHVnpuP4pJWSQCc,479
22
22
  gamspy/_algebra/condition.py,sha256=mVJeCk65dvLgYalTQHfZt0bEFFf9yoTWLXjhODf7gvg,6229
23
- gamspy/_algebra/domain.py,sha256=WN9ftyz8DPwPOtlECo_E8Cz3Qxk-0hBPGIuu5bzvX6Y,2929
24
- gamspy/_algebra/expression.py,sha256=rBG4chXXjP9AtbCkQG8XK419kOTHjLhc3l5T5o1ZGMs,25392
23
+ gamspy/_algebra/domain.py,sha256=6hBYxxZuQ-tJ5QcjEpW68SxI3i0SkgxbwgSsK93wLRc,2941
24
+ gamspy/_algebra/expression.py,sha256=yOrPeDhB-tkyfgp27U6VbyArmGQo6SnQUipyYcL8agg,25393
25
25
  gamspy/_algebra/number.py,sha256=Yf7kYLwkmXCipiAzQP7LtTchMMK7OccChuXnTIYl1w4,1680
26
26
  gamspy/_algebra/operable.py,sha256=1sFr29_7A9RKVxDlTY9fC_g4sNRavNOYpjACWzvyjRc,6315
27
- gamspy/_algebra/operation.py,sha256=e3gYxS5OPh8JYGAnAS1wEpzafFpfBt0UbcL4PKZyoY0,22990
27
+ gamspy/_algebra/operation.py,sha256=SRFHnAFRqciR5cLnrxfTQXvjYNO1fxyci5oVQAb_L6M,23235
28
28
  gamspy/_backend/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
29
29
  gamspy/_backend/backend.py,sha256=VgLL3CiFydkf1ZF8ttaOlWsDtkJIoRdi_md4tVh789Q,9217
30
30
  gamspy/_backend/engine.py,sha256=N3BIb4CDmH76CQIj87JSJBNnMLln3jsUuLy9_O5sEJ0,31826
@@ -42,18 +42,18 @@ gamspy/_cli/show.py,sha256=NTZJBtdns0Bd6UOdlTDIelhU92xmLJjT9HuDLqS3YJ4,11144
42
42
  gamspy/_cli/uninstall.py,sha256=_g3SbT6G7DZcYgtmHLhaGl1B9jY1K3D0sStifxIyqxQ,5183
43
43
  gamspy/_cli/util.py,sha256=ey3Wn7tCEggkr-sjcLqrVBda-jdadjWOn-APhSe4toE,2849
44
44
  gamspy/_symbols/__init__.py,sha256=p0Zd7XytuOe8kRclN1lFrRFaN_btFSSGdIBSXrlp03E,450
45
- gamspy/_symbols/alias.py,sha256=lIoNynvLd-tfXrVBs8Asy0TEkhs-xkTeNl4yLtzehiA,8795
46
- gamspy/_symbols/equation.py,sha256=W_8eQdZ9Mou9YVI1l0X_gwFB--TVNOK02OGpWmO3Xek,39719
47
- gamspy/_symbols/parameter.py,sha256=i5UWz8KQEHDBOjGV7JmL8GhnLnL-CiFMUhfSrBZOqQs,19721
48
- gamspy/_symbols/set.py,sha256=j3Izvgs470xiauwCbgXZOelZy-KAzslDGXuKeHn_g3g,29130
49
- gamspy/_symbols/symbol.py,sha256=Mn_O51nW99_d0G395JQ4C9SwXnMvdwqY7P6j_YyFL8E,10921
50
- gamspy/_symbols/universe_alias.py,sha256=8xoVhG9GGdl06tj56-NgN7T0dXwhp4rgKbCkjn8WhC8,4059
51
- gamspy/_symbols/variable.py,sha256=_yarP4aD-f76btWyGsGohuAgqQh1fcRWBGOngdehnic,30524
45
+ gamspy/_symbols/alias.py,sha256=BidmqvYxqMXLxU9tSJHJ484P1R-7pq9J-cP0ITNmkVc,8907
46
+ gamspy/_symbols/equation.py,sha256=6Zf0VJNTVNSAJEitIgd-5NbuycY0akMgXmLEYHnmzcU,40125
47
+ gamspy/_symbols/parameter.py,sha256=nZVE_AEbY2vyQ6OedrH8TC6ffPtSZk75MRstV-zY1z8,19833
48
+ gamspy/_symbols/set.py,sha256=aUw8AO72sEmZlH-BpGBlMXMvJHLc-Skx0cpPvPJxOag,29242
49
+ gamspy/_symbols/symbol.py,sha256=azKG8vJzrBrjGXCcKe865I5rEEbT1ZqQ02khQXPtiGs,10888
50
+ gamspy/_symbols/universe_alias.py,sha256=xs4UIvhNPOyYz2VyKcfZRCZaDLxp2fBVWzGdBFPBaJg,4566
51
+ gamspy/_symbols/variable.py,sha256=T6cMVz2pYgJIIY8_HxUkH7PgcDJhC-pMCcRPWplYe2s,30636
52
52
  gamspy/_symbols/implicits/__init__.py,sha256=bvYl_lYtnTztfNUTMxqteIztotwh1T2CUQpidQITSMQ,391
53
53
  gamspy/_symbols/implicits/implicit_equation.py,sha256=V28B9kJBpeCYekFPEvHKFwxNxkX3VEPSoMcXP6fEvzo,5070
54
54
  gamspy/_symbols/implicits/implicit_parameter.py,sha256=6w1O1-jSFH2J2S4FhssSs4ksAGwq281SodXCTzTFQQ0,8600
55
- gamspy/_symbols/implicits/implicit_set.py,sha256=ec1qXcbc1FKZ1s46cbycek7SJKrhC6LCF5FMgMLy0OE,3728
56
- gamspy/_symbols/implicits/implicit_symbol.py,sha256=YDt2DVH4WOd88gOgbqYZzq8Wm5UPRmrwg8Og4vuektM,9009
55
+ gamspy/_symbols/implicits/implicit_set.py,sha256=T-gYrvuvJwbkJxWNac8EdrMP3SQxhN5LjXqElgrm0A4,3767
56
+ gamspy/_symbols/implicits/implicit_symbol.py,sha256=dGK7vN2rCabuj1UXReO8PppbgbjcUu-lKv3IJ8_tgJo,9136
57
57
  gamspy/_symbols/implicits/implicit_variable.py,sha256=a1deGojPLXW7OLiOAOErgAGdRNlRCrBHt6r0WNchQVs,7662
58
58
  gamspy/formulations/__init__.py,sha256=-FZiCsc0Uydyc3j1nXpPvIUkB5H2dBs7FdWgFSJln2g,990
59
59
  gamspy/formulations/piecewise.py,sha256=uSyxylMEZuIhrqVdv3wuv4FAHegLf3rD9KCIdiHvALc,24421
@@ -78,12 +78,12 @@ gamspy/math/__init__.py,sha256=pyYOIEAi1I4h8gu0pCAnMh389566Ygrl0BtQh1P3OB0,3120
78
78
  gamspy/math/activation.py,sha256=XQHTKUKIT7d4-JqJdWwhyxBtnAJ6MajCjx3FWBi3k1s,24569
79
79
  gamspy/math/log_power.py,sha256=oXQmHNdaDPLdhtmv8znmSzq0MPa4fHkxGdx8hSaQq-Y,9184
80
80
  gamspy/math/matrix.py,sha256=PmYsMhF8Pq3bcgtyINryq4xV36i4KW0BtcFApOvt8GQ,16365
81
- gamspy/math/misc.py,sha256=FaSRYFjMN6AULqOqGIF6_AXDw6KVqgxm2EtZsDcBFmE,37432
81
+ gamspy/math/misc.py,sha256=qrduVOZ7CmD9wjUJx86t2J_2XMQzTz2vhd40sFwuRUk,37727
82
82
  gamspy/math/probability.py,sha256=gAVU_PTfbjXuN6i8XNjZpIYvFg8dPk1imZCbKQloSG0,4290
83
83
  gamspy/math/trigonometric.py,sha256=VBWQcP3ggl1oGDEzRs2M_T88HPJn9N0QfMJZwEUkx4w,4276
84
- gamspy-1.18.1.dist-info/licenses/LICENSE,sha256=D4HfHbHRs3LVWo3uXq44WQc1FkBP5srUCEa-vO0W9tE,1189
85
- gamspy-1.18.1.dist-info/METADATA,sha256=E7-RHCJZIutvTFYwaNd7NtgGD35BKwa-RV-IBFyoG5Y,5301
86
- gamspy-1.18.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
87
- gamspy-1.18.1.dist-info/entry_points.txt,sha256=tzgC7L0Y_BiyagBCaYOPK7lE4olRJKM_PH2inmMvj60,48
88
- gamspy-1.18.1.dist-info/top_level.txt,sha256=fsq4q5lfdb2GEZC9O3PUih38s7TfIgolIaO5NgR3Hf8,7
89
- gamspy-1.18.1.dist-info/RECORD,,
84
+ gamspy-1.18.2.dist-info/licenses/LICENSE,sha256=D4HfHbHRs3LVWo3uXq44WQc1FkBP5srUCEa-vO0W9tE,1189
85
+ gamspy-1.18.2.dist-info/METADATA,sha256=DR4AHXYISLO7i3CJUNIhjnUpkaEYk2qfCkwsM3HluUE,5301
86
+ gamspy-1.18.2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
87
+ gamspy-1.18.2.dist-info/entry_points.txt,sha256=tzgC7L0Y_BiyagBCaYOPK7lE4olRJKM_PH2inmMvj60,48
88
+ gamspy-1.18.2.dist-info/top_level.txt,sha256=fsq4q5lfdb2GEZC9O3PUih38s7TfIgolIaO5NgR3Hf8,7
89
+ gamspy-1.18.2.dist-info/RECORD,,