Skip to content

Commit 376c28e

Browse files
committed
refactor I/O system repr() processing via iosys_repr and _repr_<fmt>
1 parent 9897ab2 commit 376c28e

11 files changed

Lines changed: 169 additions & 198 deletions

File tree

control/config.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -313,7 +313,7 @@ def use_legacy_defaults(version):
313313

314314
# Verions 0.10.2
315315
if major == 0 and minor <= 10 and patch < 2:
316-
set_defaults('iosys', repr_format='loadable')
316+
set_defaults('iosys', repr_format='eval')
317317

318318
# Version 0.9.2:
319319
if major == 0 and minor < 9 or (minor == 9 and patch < 2):

control/frdata.py

Lines changed: 1 addition & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -415,37 +415,7 @@ def __str__(self):
415415

416416
return '\n'.join(outstr)
417417

418-
def __repr__(self):
419-
return self.iosys_repr(format=self.repr_format)
420-
421-
def iosys_repr(self, format='loadable'):
422-
"""Return representation of a transfer function.
423-
424-
Parameters
425-
----------
426-
format : str
427-
Format to use in creating the representation:
428-
429-
* 'iosys' : <FrequencyResponseData:sysname:[inputs]->[outputs]
430-
* 'loadable' : FrequencyResponseData(response, omega[, dt[, ...]])
431-
432-
Returns
433-
-------
434-
str
435-
String representing the transfer function.
436-
437-
Notes
438-
-----
439-
By default, the representation for a frequency response is set to
440-
'iosys'. Set config.defaults['iosys.repr_format'] to change for all
441-
I/O systems or set the `repr_format` attribute for a single system.
442-
443-
"""
444-
if format == 'iosys':
445-
return super().__repr__()
446-
elif format != 'loadable':
447-
raise ValueError(f"unknown format '{format}'")
448-
418+
def _repr_eval_(self):
449419
# Loadable format
450420
out = "FrequencyResponseData(\n{d},\n{w}{smooth}".format(
451421
d=repr(self.fresp), w=repr(self.omega),

control/iosys.py

Lines changed: 65 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
from .exception import ControlIndexError
1717

1818
__all__ = ['InputOutputSystem', 'NamedSignal', 'issiso', 'timebase',
19-
'common_timebase', 'isdtime', 'isctime']
19+
'common_timebase', 'isdtime', 'isctime', 'iosys_repr']
2020

2121
# Define module default parameter values
2222
_iosys_defaults = {
@@ -31,7 +31,7 @@
3131
'iosys.indexed_system_name_suffix': '$indexed',
3232
'iosys.converted_system_name_prefix': '',
3333
'iosys.converted_system_name_suffix': '$converted',
34-
'iosys.repr_format': 'iosys',
34+
'iosys.repr_format': 'info',
3535
}
3636

3737

@@ -163,6 +163,8 @@ class InputOutputSystem(object):
163163
Set the prefix for output signals. Default = 'y'.
164164
state_prefix : string, optional
165165
Set the prefix for state signals. Default = 'x'.
166+
repr_format : str
167+
String representation format. See :func:`control.iosys_repr`.
166168
167169
"""
168170
# Allow NDarray * IOSystem to give IOSystem._rmul_() priority
@@ -241,16 +243,30 @@ def _generic_name_check(self):
241243
nstates = None
242244

243245
def __repr__(self):
244-
return f'<{self.__class__.__name__}:{self.name}:' + \
245-
f'{list(self.input_labels)}->{list(self.output_labels)}>'
246+
return iosys_repr(self, format=self.repr_format)
246247

247-
def iosys_repr(self, format=None):
248-
raise NotImplementedError(
249-
f"`iosys_repr` is not implemented for {self.__class__}")
248+
def _repr_info_(self):
249+
return f'<{self.__class__.__name__} {self.name}: ' + \
250+
f'{list(self.input_labels)} -> {list(self.output_labels)}>'
250251

251252
@property
252253
def repr_format(self):
253-
"""Set the string representation format ('iosys' or 'loadable')."""
254+
"""String representation format.
255+
256+
Format used in creating the representation for the system:
257+
258+
* 'info' : <IOSystemType:sysname:[inputs]->[outputs]
259+
* 'eval' : system specific, loadable representation
260+
* 'latex' : latex representation of the object
261+
262+
The default representation for an input/output is set to 'info'.
263+
This value can be changed for an individual system by setting the
264+
`repr_format` parameter when the system is created or by setting
265+
the `repr_format` property after system creation. Set
266+
config.defaults['iosys.repr_format'] to change for all I/O systems
267+
or use the `repr_format` parameter/attribute for a single system.
268+
269+
"""
254270
return self._repr_format if self._repr_format is not None \
255271
else config.defaults['iosys.repr_format']
256272

@@ -740,6 +756,47 @@ def isctime(sys=None, dt=None, strict=False):
740756
return sys.isctime(strict)
741757

742758

759+
def iosys_repr(sys, format=None):
760+
"""Return representation of an I/O system.
761+
762+
Parameters
763+
----------
764+
sys : InputOutputSystem
765+
System for which the representation is generated.
766+
format : str
767+
Format to use in creating the representation:
768+
769+
* 'info' : <IOSystemType:sysname:[inputs]->[outputs]
770+
* 'eval' : system specific, loadable representation
771+
* 'latex' : latex representation of the object
772+
773+
Returns
774+
-------
775+
str
776+
String representing the input/output system.
777+
778+
Notes
779+
-----
780+
By default, the representation for an input/output is set to 'info'.
781+
Set config.defaults['iosys.repr_format'] to change for all I/O systems
782+
or use the `repr_format` parameter for a single system.
783+
784+
Jupyter will automatically use the 'latex' representation for I/O
785+
systems, when available.
786+
787+
"""
788+
format = config.defaults['iosys.repr_format'] if format is None else format
789+
match format:
790+
case 'info':
791+
return sys._repr_info_()
792+
case 'eval':
793+
return sys._repr_eval_()
794+
case 'latex':
795+
return sys._repr_latex_()
796+
case _:
797+
raise ValueError(f"format '{format}' unknown")
798+
799+
743800
# Utility function to parse iosys keywords
744801
def _process_iosys_keywords(
745802
keywords={}, defaults={}, static=False, end=False):

control/statesp.py

Lines changed: 5 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@
3434
from .frdata import FrequencyResponseData
3535
from .iosys import InputOutputSystem, NamedSignal, _process_dt_keyword, \
3636
_process_iosys_keywords, _process_signal_list, _process_subsys_index, \
37-
common_timebase, isdtime, issiso
37+
common_timebase, iosys_repr, isdtime, issiso
3838
from .lti import LTI, _process_frequency_response
3939
from .nlsys import InterconnectedSystem, NonlinearIOSystem
4040
import control
@@ -390,37 +390,7 @@ def __str__(self):
390390
string += f"\ndt = {self.dt}\n"
391391
return string
392392

393-
def __repr__(self):
394-
return self.iosys_repr(format=self.repr_format)
395-
396-
def iosys_repr(self, format='loadable'):
397-
"""Return representation of a state sapce system.
398-
399-
Parameters
400-
----------
401-
format : str
402-
Format to use in creating the representation:
403-
404-
* 'iosys' : <TransferFunction:sysname:[inputs]->[outputs]
405-
* 'loadable' : StateSpace(A, B, C, D[, dt[, ...]])
406-
407-
Returns
408-
-------
409-
str
410-
String representing the transfer function.
411-
412-
Notes
413-
-----
414-
By default, the representation for a state space system is set to
415-
'iosys'. Set config.defaults['iosys.repr_format'] to change for all
416-
I/O systems or set the `repr_format` attribute for a single system.
417-
418-
"""
419-
if format == 'iosys':
420-
return super().__repr__()
421-
elif format != 'loadable':
422-
raise ValueError(f"unknown format '{format}'")
423-
393+
def _repr_eval_(self):
424394
# Loadable format
425395
out = "StateSpace(\n{A},\n{B},\n{C},\n{D}".format(
426396
A=self.A.__repr__(), B=self.B.__repr__(),
@@ -450,6 +420,7 @@ def _latex_partitioned_stateless(self):
450420
D = eval(repr(self.D))
451421

452422
lines = [
423+
self._repr_info_(),
453424
r'$$',
454425
(r'\left('
455426
+ r'\begin{array}'
@@ -487,6 +458,7 @@ def _latex_partitioned(self):
487458
eval(repr(getattr(self, M))) for M in ['A', 'B', 'C', 'D'])
488459

489460
lines = [
461+
self._repr_info_(),
490462
r'$$',
491463
(r'\left('
492464
+ r'\begin{array}'
@@ -521,6 +493,7 @@ def _latex_separate(self):
521493
s : string with LaTeX representation of model
522494
"""
523495
lines = [
496+
self._repr_info_(),
524497
r'$$',
525498
r'\begin{array}{ll}',
526499
]

control/tests/frd_test.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -474,14 +474,14 @@ def test_repr_str(self):
474474
sysm = ct.frd(
475475
np.matmul(array([[1], [2]]), sys0.fresp), sys0.omega, name='sysm')
476476

477-
assert sys0.iosys_repr(format='loadable') == ref0
478-
assert sys1.iosys_repr(format='loadable') == ref1
477+
assert ct.iosys_repr(sys0, format='eval') == ref0
478+
assert ct.iosys_repr(sys1, format='eval') == ref1
479479

480-
sys0r = eval(sys0.iosys_repr(format='loadable'))
480+
sys0r = eval(ct.iosys_repr(sys0, format='eval'))
481481
np.testing.assert_array_almost_equal(sys0r.fresp, sys0.fresp)
482482
np.testing.assert_array_almost_equal(sys0r.omega, sys0.omega)
483483

484-
sys1r = eval(sys1.iosys_repr(format='loadable'))
484+
sys1r = eval(ct.iosys_repr(sys1, format='eval'))
485485
np.testing.assert_array_almost_equal(sys1r.fresp, sys1.fresp)
486486
np.testing.assert_array_almost_equal(sys1r.omega, sys1.omega)
487487
assert(sys1._ifunc is not None)

0 commit comments

Comments
 (0)