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
use objc2::{msg_send, rc::Retained};
use objc2_foundation::NSString;
use super::Convolution2DOpDescriptor;
use crate::{Graph, ShapeOrTensor, Tensor, ns_number_array_from_slice};
impl Graph {
// ----- Forward ----------------------------------------------------------
/// Forward 2-D convolution-transpose (a.k.a. *de-convolution*).
///
/// This op is equivalent to the data-gradient of a forward convolution.
/// A stride of `s` upsamples each spatial dimension by `s`.
///
/// The output shape may be ambiguous—`output_shape` resolves the correct
/// spatial size when multiple are mathematically valid.
///
/// # Arguments
///
/// * `source` – Input tensor.
/// * `weights` – Weights tensor.
/// * `output_shape` – Desired result shape (slice or tensor).
/// * `descriptor` – Forward-pass convolution descriptor.
/// * `name` – Optional debug label.
///
/// # Returns
///
/// A [`Tensor`] containing the convolution-transpose result.
pub fn convolution_transpose_2d<'a>(
&self,
source: &Tensor,
weights: &Tensor,
output_shape: ShapeOrTensor<'a>,
descriptor: &Convolution2DOpDescriptor,
name: Option<&str>,
) -> Retained<Tensor> {
unsafe {
match output_shape {
ShapeOrTensor::Shape(shape) => {
msg_send![self,
convolutionTranspose2DWithSourceTensor: source,
weightsTensor: weights,
outputShape: &*ns_number_array_from_slice(shape),
descriptor: descriptor,
name: name.map(NSString::from_str).as_deref(),
]
}
ShapeOrTensor::Tensor(shape_tensor) => {
msg_send![self,
convolutionTranspose2DWithSourceTensor: source,
weightsTensor: weights,
outputShapeTensor: shape_tensor,
descriptor: descriptor,
name: name.map(NSString::from_str).as_deref(),
]
}
}
}
}
// The dedicated tensor-output-shape variant is now redundant and has been removed.
// ----- Gradients --------------------------------------------------------
/// Gradient w.r.t. the **source** tensor of convolution-transpose.
///
/// # Arguments
///
/// * `incoming_gradient_tensor` – Incoming gradient (`dL/dR`).
/// * `weights_tensor` – Weights tensor from the forward pass.
/// * `output_shape` – Shape of the original source tensor.
/// * `forward_convolution_descriptor` – Descriptor used in the forward op.
/// * `name` – Optional debug label.
///
/// # Returns
///
/// A [`Tensor`] containing `dL/dS`.
pub fn convolution_transpose_2d_data_gradient<'a>(
&self,
incoming_gradient_tensor: &Tensor,
weights_tensor: &Tensor,
output_shape: ShapeOrTensor<'a>,
forward_convolution_descriptor: &Convolution2DOpDescriptor,
name: Option<&str>,
) -> Retained<Tensor> {
unsafe {
match output_shape {
ShapeOrTensor::Shape(shape) => {
msg_send![self,
convolutionTranspose2DDataGradientWithIncomingGradientTensor: incoming_gradient_tensor,
weightsTensor: weights_tensor,
outputShape: &*ns_number_array_from_slice(shape),
forwardConvolutionDescriptor: forward_convolution_descriptor,
name: name.map(NSString::from_str).as_deref()
]
}
ShapeOrTensor::Tensor(shape_tensor) => {
msg_send![self,
convolutionTranspose2DDataGradientWithIncomingGradientTensor: incoming_gradient_tensor,
weightsTensor: weights_tensor,
outputShapeTensor: shape_tensor,
forwardConvolutionDescriptor: forward_convolution_descriptor,
name: name.map(NSString::from_str).as_deref()
]
}
}
}
}
// Tensor-only overload removed.
/// Gradient w.r.t. the **weights** tensor of convolution-transpose.
///
/// # Arguments
///
/// * `incoming_gradient_tensor` – Incoming gradient.
/// * `source_tensor` – Source tensor from the forward pass.
/// * `output_shape` – Shape of the weights tensor.
/// * `forward_convolution_descriptor` – Descriptor used in the forward op.
/// * `name` – Optional debug label.
///
/// # Returns
///
/// A [`Tensor`] containing `dL/dW`.
pub fn convolution_transpose_2d_weights_gradient<'a>(
&self,
incoming_gradient_tensor: &Tensor,
source_tensor: &Tensor,
output_shape: ShapeOrTensor<'a>,
forward_convolution_descriptor: &Convolution2DOpDescriptor,
name: Option<&str>,
) -> Retained<Tensor> {
unsafe {
match output_shape {
ShapeOrTensor::Shape(shape) => {
msg_send![self,
convolutionTranspose2DWeightsGradientWithIncomingGradientTensor: incoming_gradient_tensor,
sourceTensor: source_tensor,
outputShape: &*ns_number_array_from_slice(shape),
forwardConvolutionDescriptor: forward_convolution_descriptor,
name: name.map(NSString::from_str).as_deref()
]
}
ShapeOrTensor::Tensor(shape_tensor) => {
msg_send![self,
convolutionTranspose2DWeightsGradientWithIncomingGradientTensor: incoming_gradient_tensor,
sourceTensor: source_tensor,
outputShapeTensor: shape_tensor,
forwardConvolutionDescriptor: forward_convolution_descriptor,
name: name.map(NSString::from_str).as_deref()
]
}
}
}
}
}