CompositeCost
¶
Bases: BaseCost, BaseCost1d
flowchart TD
lmlib.statespace.cost.CompositeCost[CompositeCost]
lmlib.statespace.cost.BaseCost[BaseCost]
lmlib.statespace.cost.BaseCost1d[BaseCost1d]
lmlib.statespace.cost.BaseCost --> lmlib.statespace.cost.CompositeCost
lmlib.statespace.cost.BaseCost1d --> lmlib.statespace.cost.CompositeCost
click lmlib.statespace.cost.CompositeCost href "" "lmlib.statespace.cost.CompositeCost"
click lmlib.statespace.cost.BaseCost href "" "lmlib.statespace.cost.BaseCost"
click lmlib.statespace.cost.BaseCost1d href "" "lmlib.statespace.cost.BaseCost1d"
Quadratic cost function defined by one or more ALSSMs and one or more Segments.
A CompositeCost combines multiple ALSSM models and multiple Segments in a grid,
where each row corresponds to one ALSSM and each column to one Segment.
The mapping matrix F enables or disables each ALSSM/Segment pair at each grid
node; multiple active ALSSMs in one column are superimposed.
A CompositeCost is an implementation of [Wildhaber2019] PDF (Composite Cost)
====================
Class: CompositeCost (= M ALSSM's + P Segment's)
====================
M : number of ALSSM models
^
| Segment[0] Segment[1] ... Segment[P-1]
| +------------+------------+--//--+--------------+
| alssm[0] | F[0,0] | | | F[0,P-1] |
| +------------+------------+--//--+--------------+
| . | . | ... | | . |
| . | . | ... | | . |
| +------------+------------+--//--+--------------+
| alssm[M-1] | F[M-1,0] | | | F[M-1, P-1] |
| +------------+------------+--//--+--------------+``
|------------|-------|----|------|--------------|--> k (time)
0
(0: common relative reference
index for all segments)
P : number of segments
F[m,p] in R+, scalar weight on alssm m's output in segment p.
(0 for an inactive grid node; any positive scalar for an active node.)
This figure shows the internal relationships between Segments, ALSSMs, and the
mapping matrix F.
For more details, see Chapter 9 in [Wildhaber2019]. The cost function of a composite cost is defined as
where, \(\Theta = (\theta_1, \theta_2,\dots, \theta_P)\) and the segment scalars \(\beta_p \in \mathbb{R}_+\).
Parameters:
-
alssms(iterable of ModelBase (length M), or a single ModelBase) –Set of M ALSSM models. A single ALSSM may be passed directly instead of wrapping it in an iterable (it is treated as
M = 1). -
segments(iterable of Segment (length P), or a single Segment) –Set of P Segments. A single Segment may be passed directly instead of wrapping it in an iterable (it is treated as
P = 1). -
F(array_like of shape (M, P), default:None) –Mapping matrix.
F[m, p]is the scalar weight of ALSSMmin Segmentp. Set to 0 to disable a grid node. If omitted,Fdefaults tonp.ones((M, P)); this default is only allowed when there is a single ALSSM (M = 1) or a single Segment (P = 1). With at least two ALSSMs and two Segments,Fis mandatory. -
betas(array_like of shape (P,), default:None) –Per-segment scaling factors \(\beta_p\). Default: all ones.
-
label(str, default:'n/a') –Label of this CompositeCost. Default:
'n/a'.
Examples:
>>> import lmlib as lm
>>>
>>> alssm_spike = lm.AlssmPoly(poly_degree=3, label='spike')
>>> alssm_baseline = lm.AlssmPoly(poly_degree=2, label='baseline')
>>>
>>> segment_left = lm.Segment(a=-50, b=-1, direction=lm.FORWARD, g=20, label="finite left")
>>> segment_middle = lm.Segment(a=0, b=10, direction=lm.FORWARD, g=100, label="finite middle")
>>> segment_right = lm.Segment(a=10, b=50, direction=lm.FORWARD, g=20, delta=10, label="finite right")
>>>
>>> F = [[0, 1, 0], [1, 1, 1]]
>>> cost = lm.CompositeCost((alssm_spike, alssm_baseline), (segment_left, segment_middle, segment_right), F, label='spike_baseline')
>>> print(cost)
CompositeCost(label=spike_baseline)
└- ['AlssmPoly(A=..., C=..., label=spike)', 'AlssmPoly(A=..., C=..., label=baseline)'],
└- [Segment(a=-50, ..., label=finite left), Segment(a=0, ..., label=finite middle), Segment(a=10, ..., label=finite right)]
A single ALSSM and a single Segment may be passed directly; F then
defaults to np.ones((1, 1)).
Methods:
-
get_alssm_order–Return the combined state-space order N of all ALSSMs.
-
get_alssm_output_dimension–Return the output dimension Q shared by all ALSSMs.
-
get_number_of_dimensions–Return 1 — a CompositeCost always represents a single signal dimension.
-
get_steady_state_W–Compute the steady-state Gram matrix W as a beta-weighted sum over segments.
-
eval_alssm_output–Evaluate the summed ALSSM output for a set of state vectors.
-
get_state_var_indices–Return the state indices for a named state variable in the stacked ALSSM.
-
get_alssms–Return the list of ALSSMs comprising this CompositeCost.
-
spline_H–Build the spline continuity H-matrix for a two-ALSSM cost.
Attributes:
-
label–str : Label of the CompositeCost.
-
alssms–list of ModelBase : ALSSM signal models.
-
segments–list of Segment : Window segments.
-
M–int : Number of ALSSMs \(M\).
-
P–int : Number of Segments \(P\).
-
F–ndarray: Mapping matrix \(F\), maps models to segments -
betas–ndarray of shape (P,) : Per-segment scaling factors \(\beta_p \geq 0\).
Source code in lmlib/statespace/cost.py
Methods¶
get_alssm_order
¶
get_alssm_output_dimension
¶
get_number_of_dimensions
¶
get_steady_state_W
¶
Compute the steady-state Gram matrix W as a beta-weighted sum over segments.
Parameters:
-
dim_order(ignored, default:None) –Accepted for interface compatibility; unused for 1-D costs.
-
method(str, default:'schur') –Forwarded to each
get_steady_state_W.
Returns:
-
W(ndarray of shape (N, N)) –Aggregate steady-state Gram matrix.
Source code in lmlib/statespace/cost.py
eval_alssm_output
¶
Evaluate the summed ALSSM output for a set of state vectors.
Parameters:
-
xs(array_like of shape (..., N)) –State vectors.
-
alssm_weights(None, scalar, or array_like, default:None) –Per-ALSSM output weights forwarded to
AlssmSum.
Returns:
-
out(ndarray) –Summed ALSSM output for each input state vector.
Source code in lmlib/statespace/cost.py
get_state_var_indices
¶
Return the state indices for a named state variable in the stacked ALSSM.
Parameters:
-
label(str) –State variable label.
Returns:
-
indices(list of int) –
Source code in lmlib/statespace/cost.py
get_alssms
¶
spline_H
¶
Build the spline continuity H-matrix for a two-ALSSM cost.
For a CompositeCost with exactly two ALSSMs and mixing matrix
F = [[1,0],[0,1]] (one ALSSM per segment, independent states), the full
state vector is \(x = [x_L\;(N),\; x_R\;(N)]\). The H-matrix returned
here reduces this to a lower-dimensional parameter vector \(v\) by
imposing continuity constraints at the knot:
where \(m_c =\) max_continuity and \(N\) is the model order.
Continuity constraints are derived from the k-th forward difference operator evaluated at the knot lag \(j = 0\):
Constraint of order k:
which encodes:
- \(k=0\) — value continuity: \(C x_R = C x_L\).
- \(k=1\) — first-derivative continuity: \(e_1(x_R + x_L) = 0\).
- etc.
The result is the unique minimum-rank H satisfying all constraints \(k = 0, \ldots, m_c\), computed via a stable linear solve.
Compatibility of the two ALSSMs
For constraints of order \(k \geq 1\), both ALSSMs must share the same
\(A\) and \(C\) matrices (same basis functions). If they differ and
max_continuity >= 1, a UserWarning is emitted because the
H-matrix will use the basis of alssm_index and impose it on both sides,
which is physically wrong for the other side.
For AlssmSin pairs, in particular:
max_continuity = 0(value match only) always works.max_continuity >= 1requires the sameomegaandrho = 1(undamped oscillation) on both sides; otherwise a warning is issued.
Parameters:
-
max_continuity(int) –Highest continuity order to impose.
-1returns the identity (free, no constraint);0imposes \(C^0\) (value match);D(=poly_degree) is the maximum possible (fully smooth). -
alssm_index(int, default:0) –Index (0-based) into
self.alssmsof the reference ALSSM whose \(A\) and \(C\) are used to build \(e_k\). Relevant only when the two ALSSMs differ. Default:0.
Returns:
-
H(ndarray of shape (2N, 2N - max_continuity - 1)) –Linear constraint matrix. Pass to
minimize_xasH=.
Raises:
-
ValueError–If the cost does not have exactly two ALSSMs.
Warns:
-
UserWarning–When
max_continuity >= 1and the two ALSSMs have different \(A\) or \(C\) matrices.
Examples:
Edge detection: test C^0 (offset-only) vs C^0+C^1 (slope join)::
alssm_L = lm.AlssmPolyMeixner(D, segment=segL)
alssm_R = lm.AlssmPolyMeixner(D, segment=segR)
cost = lm.CompositeCost((alssm_L, alssm_R), (segL, segR), [[1,0],[0,1]])
rls.filter(y)
xs_free = rls.minimize_x() # unconstrained
xs_cont = rls.minimize_x(H=cost.spline_H(0)) # C^0 join
xs_peak = rls.minimize_x(H=cost.spline_H(1)) # C^0+C^1 join
xs_full = rls.minimize_x(H=cost.spline_H(D)) # fully smooth
Works equally for AlssmPoly, AlssmPolyJordan, AlssmPolyLegendre, AlssmSin.
For AlssmPoly(1):
spline_H(0) reproduces H_Continuous and
spline_H(1) reproduces H_Peak from the literature.
Source code in lmlib/statespace/cost.py
621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 | |