Skip to content

Commit 1434540

Browse files
committed
add repr_gallery + update formatting for style and consistency
1 parent ea7d3ed commit 1434540

5 files changed

Lines changed: 273 additions & 115 deletions

File tree

control/frdata.py

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -399,16 +399,19 @@ def __str__(self):
399399
"""String representation of the transfer function."""
400400

401401
mimo = self.ninputs > 1 or self.noutputs > 1
402-
outstr = [f"{InputOutputSystem.__str__(self)}", ""]
402+
outstr = [f"{InputOutputSystem.__str__(self)}"]
403+
nl = "\n " if mimo else "\n"
404+
sp = " " if mimo else ""
403405

404406
for i in range(self.ninputs):
405407
for j in range(self.noutputs):
406408
if mimo:
407-
outstr.append("Input %i to output %i:" % (i + 1, j + 1))
408-
outstr.append('Freq [rad/s] Response')
409-
outstr.append('------------ ---------------------')
409+
outstr.append(
410+
"\nInput %i to output %i:" % (i + 1, j + 1))
411+
outstr.append(nl + 'Freq [rad/s] Response')
412+
outstr.append(sp + '------------ ---------------------')
410413
outstr.extend(
411-
['%12.3f %10.4g%+10.4gj' % (w, re, im)
414+
[sp + '%12.3f %10.4g%+10.4gj' % (w, re, im)
412415
for w, re, im in zip(self.omega,
413416
real(self.fresp[j, i, :]),
414417
imag(self.fresp[j, i, :]))])
@@ -421,10 +424,7 @@ def _repr_eval_(self):
421424
d=repr(self.fresp), w=repr(self.omega),
422425
smooth=(self._ifunc and ", smooth=True") or "")
423426

424-
if config.defaults['control.default_dt'] != self.dt:
425-
out += ",\ndt={dt}".format(
426-
dt='None' if self.dt is None else self.dt)
427-
427+
out += self._dt_repr()
428428
if len(labels := self._label_repr()) > 0:
429429
out += ",\n" + labels
430430

control/iosys.py

Lines changed: 55 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -242,12 +242,51 @@ def _generic_name_check(self):
242242
#: :meta hide-value:
243243
nstates = None
244244

245+
#
246+
# System representation
247+
#
248+
249+
def __str__(self):
250+
"""String representation of an input/output object"""
251+
out = f"<{self.__class__.__name__}>: {self.name}"
252+
out += f"\nInputs ({self.ninputs}): {self.input_labels}"
253+
out += f"\nOutputs ({self.noutputs}): {self.output_labels}"
254+
if self.nstates is not None:
255+
out += f"\nStates ({self.nstates}): {self.state_labels}"
256+
out += self._dt_repr("\n")
257+
return out
258+
245259
def __repr__(self):
246260
return iosys_repr(self, format=self.repr_format)
247261

248-
def _repr_info_(self):
249-
return f'<{self.__class__.__name__} {self.name}: ' + \
250-
f'{list(self.input_labels)} -> {list(self.output_labels)}>'
262+
def _repr_info_(self, html=False):
263+
out = f"<{self.__class__.__name__} {self.name}: " + \
264+
f"{list(self.input_labels)} -> {list(self.output_labels)}"
265+
out += self._dt_repr(", ") + ">"
266+
267+
if html:
268+
# Replace symbols that might be interpreted by HTML processing
269+
escape_chars = {
270+
'$': r'\$',
271+
'<': '&lt;',
272+
'>': '&gt;',
273+
}
274+
return "".join([c if c not in escape_chars else
275+
escape_chars[c] for c in out])
276+
else:
277+
return out
278+
279+
def _repr_eval_(self):
280+
# Defaults to _repr_info_; override in subclasses
281+
return self._repr_info_()
282+
283+
def _repr_latex_(self):
284+
# Defaults to using __repr__; override in subclasses
285+
return None
286+
287+
def _repr_html_(self):
288+
# Defaults to using __repr__; override in subclasses
289+
return None
251290

252291
@property
253292
def repr_format(self):
@@ -257,7 +296,7 @@ def repr_format(self):
257296
258297
* 'info' : <IOSystemType:sysname:[inputs]->[outputs]
259298
* 'eval' : system specific, loadable representation
260-
* 'latex' : latex representation of the object
299+
* 'latex' : HTML/LaTeX representation of the object
261300
262301
The default representation for an input/output is set to 'info'.
263302
This value can be changed for an individual system by setting the
@@ -274,15 +313,6 @@ def repr_format(self):
274313
def repr_format(self, value):
275314
self._repr_format = value
276315

277-
def __str__(self):
278-
"""String representation of an input/output object"""
279-
out = f"<{self.__class__.__name__}>: {self.name}"
280-
out += f"\nInputs ({self.ninputs}): {self.input_labels}"
281-
out += f"\nOutputs ({self.noutputs}): {self.output_labels}"
282-
if self.nstates is not None:
283-
out += f"\nStates ({self.nstates}): {self.state_labels}"
284-
return out
285-
286316
def _label_repr(self, show_count=True):
287317
out, count = "", 0
288318

@@ -306,6 +336,8 @@ def _label_repr(self, show_count=True):
306336
spec = f"{sig_name}={sig_labels}"
307337
elif show_count:
308338
spec = f"{sig_name}={len(sig_labels)}"
339+
else:
340+
spec = ""
309341

310342
# Append the specification string to the output, with wrapping
311343
if count == 0:
@@ -314,13 +346,20 @@ def _label_repr(self, show_count=True):
314346
# TODO: check to make sure a single line is enough (minor)
315347
out += ",\n"
316348
count = len(spec)
317-
else:
349+
elif len(spec) > 0:
318350
out += ", "
319351
count += len(spec) + 2
320352
out += spec
321353

322354
return out
323355

356+
def _dt_repr(self, separator="\n"):
357+
if config.defaults['control.default_dt'] != self.dt:
358+
return "{separator}dt={dt}".format(
359+
separator=separator, dt='None' if self.dt is None else self.dt)
360+
else:
361+
return ""
362+
324363
# Find a list of signals by name, index, or pattern
325364
def _find_signals(self, name_list, sigdict):
326365
if not isinstance(name_list, (list, tuple)):
@@ -768,7 +807,7 @@ def iosys_repr(sys, format=None):
768807
769808
* 'info' : <IOSystemType:sysname:[inputs]->[outputs]
770809
* 'eval' : system specific, loadable representation
771-
* 'latex' : latex representation of the object
810+
* 'latex' : HTML/LaTeX representation of the object
772811
773812
Returns
774813
-------
@@ -792,7 +831,7 @@ def iosys_repr(sys, format=None):
792831
case 'eval':
793832
return sys._repr_eval_()
794833
case 'latex':
795-
return sys._repr_latex_()
834+
return sys._repr_html_()
796835
case _:
797836
raise ValueError(f"format '{format}' unknown")
798837

control/statesp.py

Lines changed: 39 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -132,12 +132,12 @@ class StateSpace(NonlinearIOSystem, LTI):
132132
signal offsets. The subsystem is created by truncating the inputs and
133133
outputs, but leaving the full set of system states.
134134
135-
StateSpace instances have support for IPython LaTeX output, intended
136-
for pretty-printing in Jupyter notebooks. The LaTeX output can be
135+
StateSpace instances have support for IPython HTML/LaTeX output, intended
136+
for pretty-printing in Jupyter notebooks. The HTML/LaTeX output can be
137137
configured using `control.config.defaults['statesp.latex_num_format']`
138-
and `control.config.defaults['statesp.latex_repr_type']`. The LaTeX
139-
output is tailored for MathJax, as used in Jupyter, and may look odd
140-
when typeset by non-MathJax LaTeX systems.
138+
and `control.config.defaults['statesp.latex_repr_type']`. The
139+
HTML/LaTeX output is tailored for MathJax, as used in Jupyter, and
140+
may look odd when typeset by non-MathJax LaTeX systems.
141141
142142
`control.config.defaults['statesp.latex_num_format']` is a format string
143143
fragment, specifically the part of the format string after `'{:'`
@@ -386,8 +386,6 @@ def __str__(self):
386386
"\n ".join(str(M).splitlines()))
387387
for Mvar, M in zip(["A", "B", "C", "D"],
388388
[self.A, self.B, self.C, self.D])])
389-
if self.isdtime(strict=True):
390-
string += f"\ndt = {self.dt}\n"
391389
return string
392390

393391
def _repr_eval_(self):
@@ -396,16 +394,42 @@ def _repr_eval_(self):
396394
A=self.A.__repr__(), B=self.B.__repr__(),
397395
C=self.C.__repr__(), D=self.D.__repr__())
398396

399-
if config.defaults['control.default_dt'] != self.dt:
400-
out += ",\ndt={dt}".format(
401-
dt='None' if self.dt is None else self.dt)
402-
403-
if len(labels := self._label_repr()) > 0:
397+
out += super()._dt_repr(",\n")
398+
if len(labels := super()._label_repr(show_count=False)) > 0:
404399
out += ",\n" + labels
405400

406401
out += ")"
407402
return out
408403

404+
def _repr_html_(self):
405+
"""HTML representation of state-space model.
406+
407+
Output is controlled by config options statesp.latex_repr_type,
408+
statesp.latex_num_format, and statesp.latex_maxsize.
409+
410+
The output is primarily intended for Jupyter notebooks, which
411+
use MathJax to render the LaTeX, and the results may look odd
412+
when processed by a 'conventional' LaTeX system.
413+
414+
Returns
415+
-------
416+
s : string
417+
HTML/LaTeX representation of model, or None if either matrix
418+
dimension is greater than statesp.latex_maxsize.
419+
420+
"""
421+
syssize = self.nstates + max(self.noutputs, self.ninputs)
422+
if syssize > config.defaults['statesp.latex_maxsize']:
423+
return None
424+
elif config.defaults['statesp.latex_repr_type'] == 'partitioned':
425+
return super()._repr_info(html=True) + self._latex_partitioned()
426+
elif config.defaults['statesp.latex_repr_type'] == 'separate':
427+
return super()._repr_info(html=True) + self._latex_separate()
428+
else:
429+
raise ValueError(
430+
"Unknown statesp.latex_repr_type '{cfg}'".format(
431+
cfg=config.defaults['statesp.latex_repr_type']))
432+
409433
def _latex_partitioned_stateless(self):
410434
"""`Partitioned` matrix LaTeX representation for stateless systems
411435
@@ -420,7 +444,6 @@ def _latex_partitioned_stateless(self):
420444
D = eval(repr(self.D))
421445

422446
lines = [
423-
self._repr_info_(),
424447
r'$$',
425448
(r'\left('
426449
+ r'\begin{array}'
@@ -433,8 +456,7 @@ def _latex_partitioned_stateless(self):
433456

434457
lines.extend([
435458
r'\end{array}'
436-
r'\right)'
437-
+ self._latex_dt(),
459+
r'\right)',
438460
r'$$'])
439461

440462
return '\n'.join(lines)
@@ -458,7 +480,6 @@ def _latex_partitioned(self):
458480
eval(repr(getattr(self, M))) for M in ['A', 'B', 'C', 'D'])
459481

460482
lines = [
461-
self._repr_info_(),
462483
r'$$',
463484
(r'\left('
464485
+ r'\begin{array}'
@@ -477,8 +498,7 @@ def _latex_partitioned(self):
477498

478499
lines.extend([
479500
r'\end{array}'
480-
+ r'\right)'
481-
+ self._latex_dt(),
501+
+ r'\right)',
482502
r'$$'])
483503

484504
return '\n'.join(lines)
@@ -493,7 +513,6 @@ def _latex_separate(self):
493513
s : string with LaTeX representation of model
494514
"""
495515
lines = [
496-
self._repr_info_(),
497516
r'$$',
498517
r'\begin{array}{ll}',
499518
]
@@ -522,52 +541,11 @@ def fmt_matrix(matrix, name):
522541
lines.extend(fmt_matrix(self.D, 'D'))
523542

524543
lines.extend([
525-
r'\end{array}'
526-
+ self._latex_dt(),
544+
r'\end{array}',
527545
r'$$'])
528546

529547
return '\n'.join(lines)
530548

531-
def _latex_dt(self):
532-
if self.isdtime(strict=True):
533-
if self.dt is True:
534-
return r"~,~dt=~\mathrm{True}"
535-
else:
536-
fmt = config.defaults['statesp.latex_num_format']
537-
return f"~,~dt={self.dt:{fmt}}"
538-
return ""
539-
540-
def _repr_latex_(self):
541-
"""LaTeX representation of state-space model
542-
543-
Output is controlled by config options statesp.latex_repr_type,
544-
statesp.latex_num_format, and statesp.latex_maxsize.
545-
546-
The output is primarily intended for Jupyter notebooks, which
547-
use MathJax to render the LaTeX, and the results may look odd
548-
when processed by a 'conventional' LaTeX system.
549-
550-
551-
Returns
552-
-------
553-
554-
s : string with LaTeX representation of model, or None if
555-
either matrix dimension is greater than
556-
statesp.latex_maxsize
557-
558-
"""
559-
syssize = self.nstates + max(self.noutputs, self.ninputs)
560-
if syssize > config.defaults['statesp.latex_maxsize']:
561-
return None
562-
elif config.defaults['statesp.latex_repr_type'] == 'partitioned':
563-
return self._latex_partitioned()
564-
elif config.defaults['statesp.latex_repr_type'] == 'separate':
565-
return self._latex_separate()
566-
else:
567-
raise ValueError(
568-
"Unknown statesp.latex_repr_type '{cfg}'".format(
569-
cfg=config.defaults['statesp.latex_repr_type']))
570-
571549
# Negation of a system
572550
def __neg__(self):
573551
"""Negate a state space system."""

0 commit comments

Comments
 (0)