1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
/*
* SPDX-License-Identifier: MIT
* Copyright (c) 2023 - 2026. The DeepCausality Authors and Contributors. All Rights Reserved.
*/
//! Isomorphism between rank-2 [`CausalTensor<F>`] and [`CsrMatrix<F>`].
//!
//! # Not feature-gated, and no longer in the sparse crate
//!
//! Both facts changed and the text here described neither. The conversion was once behind a
//! `tensor-iso` feature in `deep_causality_sparse`, so that a caller wanting sparse matrices
//! without tensors did not pay to compile `deep_causality_tensor`. That crate is retired, this
//! file moved here, and the dependency now runs the other way: `deep_causality_tensor` depends on
//! `deep_causality_linear`, which owns `CsrMatrix`. Nothing is gated, and no feature enables it.
//!
//! Lives under `src/extensions/` (alongside `ext_hkt`) following the convention that anything
//! bridging this crate to another DC crate is an `ext_*` extension.
//!
//! ## Design (mixed-tier)
//!
//! The dependency direction decides where each half can live. The iso ships in two pieces, both
//! rooted in this crate:
//!
//! - **Forward** (`CausalTensor<F>` -> `CsrMatrix<F>`): Tier 1
//! `impl TryFrom<CausalTensor<F>> for CsrMatrix<F>`. Returns
//! [`CsrFromTensorError`] on rank ≠ 2. Uses `TryFrom` (not `From`)
//! because the conversion is intrinsically partial — only rank-2
//! tensors are matrices.
//! - **Reverse** (`CsrMatrix<F>` -> `CausalTensor<F>`): Tier 2
//! `impl Iso<CsrMatrix<F>, CausalTensor<F>> for CsrMatrix<F>` with
//! `CsrMatrix<F>` as `Self` (orphan-safe). An inherent ergonomic
//! alias `CsrMatrix::to_dense()` delegates to the iso's `to_target`.
//!
//! ## What's NOT here
//!
//! No marker subtraits. Neither `CausalTensor<F>` nor `CsrMatrix<F>`
//! currently implements `Group`/`Ring`/`Field`, so `GroupIso`,
//! `RingIso`, etc. would not type-check. The base `Iso<S, T>` is the
//! right surface.
use crateCausalTensor;
use vec;
use Vec;
use fmt;
use Semiring;
use Iso;
use CsrMatrix;
/// Error returned by [`CsrMatrix::try_from`] when the input
/// [`CausalTensor`] has a rank other than 2.
///
/// The forward direction of the tensor / sparse iso is intrinsically
/// partial: only rank-2 tensors are matrices. `From` is reserved for
/// total conversions, so the surface uses `TryFrom` instead.
// =============================================================================
// Forward (Tier 1): CausalTensor<F> -> CsrMatrix<F> via TryFrom
// =============================================================================
/// The scalar bound is [`Semiring`]. Both directions read coefficients, compare them against
/// `F::zero()`, and sum duplicates. Neither multiplies, so multiplicative commutativity is
/// irrelevant to them. `Quaternion<f64>` is a `Semiring` without multiplicative commutativity, and
/// it converts and round-trips under this bound.
// =============================================================================
// Reverse (Tier 2): Iso<CsrMatrix<F>, CausalTensor<F>> on CsrMatrix as Self
// =============================================================================
// =============================================================================
// Ergonomic alias, as an extension trait
// =============================================================================
/// `to_dense` for a sparse matrix, as an extension trait.
///
/// # Why a trait rather than an inherent method
///
/// `CsrMatrix` belongs to `deep_causality_linear` and this conversion belongs here — a crate cannot
/// write an inherent `impl` for a type it does not own (E0116). The trait impl is allowed because
/// `CausalTensor` is local, which is the same reason the [`TryFrom`] and [`Iso`] impls above can
/// live here. A caller brings the method into scope with a `use`.