Skip to content

Commit 0a7cd53

Browse files
committed
discrete time LaTeX repr of StateSpace systems
1 parent 15dc5ce commit 0a7cd53

File tree

2 files changed

+35
-16
lines changed

2 files changed

+35
-16
lines changed

control/statesp.py

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -418,8 +418,8 @@ def _latex_partitioned_stateless(self):
418418
"""
419419
lines = [
420420
r'\[',
421-
r'\left(',
422-
(r'\begin{array}'
421+
(r'\left('
422+
+ r'\begin{array}'
423423
+ r'{' + 'rll' * self.ninputs + '}')
424424
]
425425

@@ -429,7 +429,8 @@ def _latex_partitioned_stateless(self):
429429

430430
lines.extend([
431431
r'\end{array}'
432-
r'\right)',
432+
r'\right)'
433+
+ self._latex_dt(),
433434
r'\]'])
434435

435436
return '\n'.join(lines)
@@ -449,8 +450,8 @@ def _latex_partitioned(self):
449450

450451
lines = [
451452
r'\[',
452-
r'\left(',
453-
(r'\begin{array}'
453+
(r'\left('
454+
+ r'\begin{array}'
454455
+ r'{' + 'rll' * self.nstates + '|' + 'rll' * self.ninputs + '}')
455456
]
456457

@@ -466,7 +467,8 @@ def _latex_partitioned(self):
466467

467468
lines.extend([
468469
r'\end{array}'
469-
r'\right)',
470+
+ r'\right)'
471+
+ self._latex_dt(),
470472
r'\]'])
471473

472474
return '\n'.join(lines)
@@ -509,11 +511,21 @@ def fmt_matrix(matrix, name):
509511
lines.extend(fmt_matrix(self.D, 'D'))
510512

511513
lines.extend([
512-
r'\end{array}',
514+
r'\end{array}'
515+
+ self._latex_dt(),
513516
r'\]'])
514517

515518
return '\n'.join(lines)
516519

520+
def _latex_dt(self):
521+
if self.isdtime(strict=True):
522+
if self.dt is True:
523+
return r"~,~dt~\mathrm{unspecified}"
524+
else:
525+
fmt = config.defaults['statesp.latex_num_format']
526+
return f"~,~dt={self.dt:{fmt}}"
527+
return ""
528+
517529
def _repr_latex_(self):
518530
"""LaTeX representation of state-space model
519531
@@ -534,9 +546,9 @@ def _repr_latex_(self):
534546
elif config.defaults['statesp.latex_repr_type'] == 'separate':
535547
return self._latex_separate()
536548
else:
537-
cfg = config.defaults['statesp.latex_repr_type']
538549
raise ValueError(
539-
"Unknown statesp.latex_repr_type '{cfg}'".format(cfg=cfg))
550+
"Unknown statesp.latex_repr_type '{cfg}'".format(
551+
cfg=config.defaults['statesp.latex_repr_type']))
540552

541553
# Negation of a system
542554
def __neg__(self):

control/tests/statesp_test.py

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -997,19 +997,19 @@ def test_statespace_defaults(self, matarrayout):
997997
[[1.2345, -2e-200], [-1, 0]])
998998

999999
LTX_G1_REF = {
1000-
'p3_p' : '\\[\n\\left(\n\\begin{array}{rllrll|rll}\n3.&\\hspace{-1em}14&\\hspace{-1em}\\phantom{\\cdot}&1\\phantom{.}&\\hspace{-1em}&\\hspace{-1em}\\cdot10^{100}&0\\phantom{.}&\\hspace{-1em}&\\hspace{-1em}\\phantom{\\cdot}\\\\\n-1.&\\hspace{-1em}23&\\hspace{-1em}\\phantom{\\cdot}&5\\phantom{.}&\\hspace{-1em}&\\hspace{-1em}\\cdot10^{-23}&1\\phantom{.}&\\hspace{-1em}&\\hspace{-1em}\\phantom{\\cdot}\\\\\n\\hline\n9.&\\hspace{-1em}88&\\hspace{-1em}\\cdot10^{8}&0.&\\hspace{-1em}00123&\\hspace{-1em}\\phantom{\\cdot}&5\\phantom{.}&\\hspace{-1em}&\\hspace{-1em}\\phantom{\\cdot}\\\\\n\\end{array}\\right)\n\\]',
1000+
'p3_p' : '\\[\n\\left(\\begin{array}{rllrll|rll}\n3.&\\hspace{-1em}14&\\hspace{-1em}\\phantom{\\cdot}&1\\phantom{.}&\\hspace{-1em}&\\hspace{-1em}\\cdot10^{100}&0\\phantom{.}&\\hspace{-1em}&\\hspace{-1em}\\phantom{\\cdot}\\\\\n-1.&\\hspace{-1em}23&\\hspace{-1em}\\phantom{\\cdot}&5\\phantom{.}&\\hspace{-1em}&\\hspace{-1em}\\cdot10^{-23}&1\\phantom{.}&\\hspace{-1em}&\\hspace{-1em}\\phantom{\\cdot}\\\\\n\\hline\n9.&\\hspace{-1em}88&\\hspace{-1em}\\cdot10^{8}&0.&\\hspace{-1em}00123&\\hspace{-1em}\\phantom{\\cdot}&5\\phantom{.}&\\hspace{-1em}&\\hspace{-1em}\\phantom{\\cdot}\\\\\n\\end{array}\\right)\n\\]',
10011001

1002-
'p5_p' : '\\[\n\\left(\n\\begin{array}{rllrll|rll}\n3.&\\hspace{-1em}1416&\\hspace{-1em}\\phantom{\\cdot}&1\\phantom{.}&\\hspace{-1em}&\\hspace{-1em}\\cdot10^{100}&0\\phantom{.}&\\hspace{-1em}&\\hspace{-1em}\\phantom{\\cdot}\\\\\n-1.&\\hspace{-1em}2346&\\hspace{-1em}\\phantom{\\cdot}&5\\phantom{.}&\\hspace{-1em}&\\hspace{-1em}\\cdot10^{-23}&1\\phantom{.}&\\hspace{-1em}&\\hspace{-1em}\\phantom{\\cdot}\\\\\n\\hline\n9.&\\hspace{-1em}8765&\\hspace{-1em}\\cdot10^{8}&0.&\\hspace{-1em}001234&\\hspace{-1em}\\phantom{\\cdot}&5\\phantom{.}&\\hspace{-1em}&\\hspace{-1em}\\phantom{\\cdot}\\\\\n\\end{array}\\right)\n\\]',
1002+
'p5_p' : '\\[\n\\left(\\begin{array}{rllrll|rll}\n3.&\\hspace{-1em}1416&\\hspace{-1em}\\phantom{\\cdot}&1\\phantom{.}&\\hspace{-1em}&\\hspace{-1em}\\cdot10^{100}&0\\phantom{.}&\\hspace{-1em}&\\hspace{-1em}\\phantom{\\cdot}\\\\\n-1.&\\hspace{-1em}2346&\\hspace{-1em}\\phantom{\\cdot}&5\\phantom{.}&\\hspace{-1em}&\\hspace{-1em}\\cdot10^{-23}&1\\phantom{.}&\\hspace{-1em}&\\hspace{-1em}\\phantom{\\cdot}\\\\\n\\hline\n9.&\\hspace{-1em}8765&\\hspace{-1em}\\cdot10^{8}&0.&\\hspace{-1em}001234&\\hspace{-1em}\\phantom{\\cdot}&5\\phantom{.}&\\hspace{-1em}&\\hspace{-1em}\\phantom{\\cdot}\\\\\n\\end{array}\\right)\n\\]',
10031003

10041004
'p3_s' : '\\[\n\\begin{array}{ll}\nA = \\left(\\begin{array}{rllrll}\n3.&\\hspace{-1em}14&\\hspace{-1em}\\phantom{\\cdot}&1\\phantom{.}&\\hspace{-1em}&\\hspace{-1em}\\cdot10^{100}\\\\\n-1.&\\hspace{-1em}23&\\hspace{-1em}\\phantom{\\cdot}&5\\phantom{.}&\\hspace{-1em}&\\hspace{-1em}\\cdot10^{-23}\\\\\n\\end{array}\\right)\n&\nB = \\left(\\begin{array}{rll}\n0\\phantom{.}&\\hspace{-1em}&\\hspace{-1em}\\phantom{\\cdot}\\\\\n1\\phantom{.}&\\hspace{-1em}&\\hspace{-1em}\\phantom{\\cdot}\\\\\n\\end{array}\\right)\n\\\\\nC = \\left(\\begin{array}{rllrll}\n9.&\\hspace{-1em}88&\\hspace{-1em}\\cdot10^{8}&0.&\\hspace{-1em}00123&\\hspace{-1em}\\phantom{\\cdot}\\\\\n\\end{array}\\right)\n&\nD = \\left(\\begin{array}{rll}\n5\\phantom{.}&\\hspace{-1em}&\\hspace{-1em}\\phantom{\\cdot}\\\\\n\\end{array}\\right)\n\\end{array}\n\\]',
10051005

10061006
'p5_s' : '\\[\n\\begin{array}{ll}\nA = \\left(\\begin{array}{rllrll}\n3.&\\hspace{-1em}1416&\\hspace{-1em}\\phantom{\\cdot}&1\\phantom{.}&\\hspace{-1em}&\\hspace{-1em}\\cdot10^{100}\\\\\n-1.&\\hspace{-1em}2346&\\hspace{-1em}\\phantom{\\cdot}&5\\phantom{.}&\\hspace{-1em}&\\hspace{-1em}\\cdot10^{-23}\\\\\n\\end{array}\\right)\n&\nB = \\left(\\begin{array}{rll}\n0\\phantom{.}&\\hspace{-1em}&\\hspace{-1em}\\phantom{\\cdot}\\\\\n1\\phantom{.}&\\hspace{-1em}&\\hspace{-1em}\\phantom{\\cdot}\\\\\n\\end{array}\\right)\n\\\\\nC = \\left(\\begin{array}{rllrll}\n9.&\\hspace{-1em}8765&\\hspace{-1em}\\cdot10^{8}&0.&\\hspace{-1em}001234&\\hspace{-1em}\\phantom{\\cdot}\\\\\n\\end{array}\\right)\n&\nD = \\left(\\begin{array}{rll}\n5\\phantom{.}&\\hspace{-1em}&\\hspace{-1em}\\phantom{\\cdot}\\\\\n\\end{array}\\right)\n\\end{array}\n\\]',
10071007
}
10081008

10091009
LTX_G2_REF = {
1010-
'p3_p' : '\\[\n\\left(\n\\begin{array}{rllrll}\n1.&\\hspace{-1em}23&\\hspace{-1em}\\phantom{\\cdot}&-2\\phantom{.}&\\hspace{-1em}&\\hspace{-1em}\\cdot10^{-200}\\\\\n-1\\phantom{.}&\\hspace{-1em}&\\hspace{-1em}\\phantom{\\cdot}&0\\phantom{.}&\\hspace{-1em}&\\hspace{-1em}\\phantom{\\cdot}\\\\\n\\end{array}\\right)\n\\]',
1010+
'p3_p' : '\\[\n\\left(\\begin{array}{rllrll}\n1.&\\hspace{-1em}23&\\hspace{-1em}\\phantom{\\cdot}&-2\\phantom{.}&\\hspace{-1em}&\\hspace{-1em}\\cdot10^{-200}\\\\\n-1\\phantom{.}&\\hspace{-1em}&\\hspace{-1em}\\phantom{\\cdot}&0\\phantom{.}&\\hspace{-1em}&\\hspace{-1em}\\phantom{\\cdot}\\\\\n\\end{array}\\right)\n\\]',
10111011

1012-
'p5_p' : '\\[\n\\left(\n\\begin{array}{rllrll}\n1.&\\hspace{-1em}2345&\\hspace{-1em}\\phantom{\\cdot}&-2\\phantom{.}&\\hspace{-1em}&\\hspace{-1em}\\cdot10^{-200}\\\\\n-1\\phantom{.}&\\hspace{-1em}&\\hspace{-1em}\\phantom{\\cdot}&0\\phantom{.}&\\hspace{-1em}&\\hspace{-1em}\\phantom{\\cdot}\\\\\n\\end{array}\\right)\n\\]',
1012+
'p5_p' : '\\[\n\\left(\\begin{array}{rllrll}\n1.&\\hspace{-1em}2345&\\hspace{-1em}\\phantom{\\cdot}&-2\\phantom{.}&\\hspace{-1em}&\\hspace{-1em}\\cdot10^{-200}\\\\\n-1\\phantom{.}&\\hspace{-1em}&\\hspace{-1em}\\phantom{\\cdot}&0\\phantom{.}&\\hspace{-1em}&\\hspace{-1em}\\phantom{\\cdot}\\\\\n\\end{array}\\right)\n\\]',
10131013

10141014
'p3_s' : '\\[\n\\begin{array}{ll}\nD = \\left(\\begin{array}{rllrll}\n1.&\\hspace{-1em}23&\\hspace{-1em}\\phantom{\\cdot}&-2\\phantom{.}&\\hspace{-1em}&\\hspace{-1em}\\cdot10^{-200}\\\\\n-1\\phantom{.}&\\hspace{-1em}&\\hspace{-1em}\\phantom{\\cdot}&0\\phantom{.}&\\hspace{-1em}&\\hspace{-1em}\\phantom{\\cdot}\\\\\n\\end{array}\\right)\n\\end{array}\n\\]',
10151015

@@ -1022,9 +1022,14 @@ def test_statespace_defaults(self, matarrayout):
10221022
@pytest.mark.parametrize(" gmats, ref",
10231023
[(LTX_G1, LTX_G1_REF),
10241024
(LTX_G2, LTX_G2_REF)])
1025+
@pytest.mark.parametrize("dt, dtref",
1026+
[(0, ""),
1027+
(None, ""),
1028+
(True, r"~,~dt~\mathrm{{unspecified}}"),
1029+
(0.1, r"~,~dt={dt:{fmt}}")])
10251030
@pytest.mark.parametrize("repr_type", [None, "partitioned", "separate"])
10261031
@pytest.mark.parametrize("num_format", [None, ".3g", ".5g"])
1027-
def test_latex_repr(gmats, ref, repr_type, num_format, editsdefaults):
1032+
def test_latex_repr(gmats, ref, dt, dtref, repr_type, num_format, editsdefaults):
10281033
"""Test `._latex_repr_` with different config values
10291034
10301035
This is a 'gold image' test, so if you change behaviour,
@@ -1040,9 +1045,11 @@ def test_latex_repr(gmats, ref, repr_type, num_format, editsdefaults):
10401045
if repr_type is not None:
10411046
set_defaults('statesp', latex_repr_type=repr_type)
10421047

1043-
g = StateSpace(*gmats)
1048+
g = StateSpace(*(gmats+(dt,)))
10441049
refkey = "{}_{}".format(refkey_n[num_format], refkey_r[repr_type])
1045-
assert g._repr_latex_() == ref[refkey]
1050+
dt_latex = dtref.format(dt=dt, fmt=defaults['statesp.latex_num_format'])
1051+
ref_latex = ref[refkey][:-3] + dt_latex + ref[refkey][-3:]
1052+
assert g._repr_latex_() == ref_latex
10461053

10471054

10481055
@pytest.mark.parametrize(

0 commit comments

Comments
 (0)