-
Notifications
You must be signed in to change notification settings - Fork 10.5k
[AutoDiff] Support differentiation of wrapped properties. #31173
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[AutoDiff] Support differentiation of wrapped properties. #31173
Conversation
"requires all stored properties to be mutable; use 'var' instead, or add " | ||
"an explicit '@noDerivative' attribute" | ||
"requires all stored properties not marked with `@noDerivative` to be " | ||
"mutable; add '%0.wrappedValue.set', or add an explicit '@noDerivative' " |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
.set
is not a user-facing identifier today. I'd suggest describing this in plain English, e.g. "a setter for 'Foo.wrappedValue'".- Suggesting that users add a setter isn't always helpful. Users are most likely using a property wrapper that they did not define, and adding a setter isn't something that can be done retroactively. I'd suggest removing this suggestion.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- I'd suggest removing this suggestion.
Done in f4b389c.
Alternatively, we could conditionally suggest "adding a setter for 'Wrapper.wrappedValue'" iff the wrapper type is defined in the same file or module.
f4b389c
to
7f49407
Compare
Force-pushed to squash minor commits. Testing now. |
@swift-ci Please test |
Build failed |
There's an unrelated macOS test failure, for other PRs too.
I'll just trigger smoke tests. |
Differentiable conformance derivation now "peers through" property wrappers. Synthesized TangentVector structs contain wrapped properties' TangentVectors as stored properties, not wrappers' TangentVectors. Property wrapper types are not required to conform to `Differentiable`. Property wrapper types are required to provide `wrappedValue.set`, which is needed to synthesize `mutating func move(along:)`. ``` import _Differentiation @propertyWrapper struct Wrapper<Value> { var wrappedValue: Value } struct Struct: Differentiable { @wrapper var x: Float = 0 // Compiler now synthesizes: // struct TangentVector: Differentiable & AdditiveArithmetic { // var x: Float // ... // } } ``` Resolves SR-12638.
Support differentiation of property wrapper wrapped value getters and setters. Create new pullback generation code path for "semantic member accessors". "Semantic member accessors" are attached to member properties that have a corresponding tangent stored property in the parent `TangentVector` type. These accessors have special-case pullback generation based on their semantic behavior. Currently, only getters and setters are supported. This special-case pullback generation is currently used for stored property accessors and property wrapper wrapped value accessors. In the future, it can also be used to support `@differentiable(useInTangentVector)` computed properties: SR-12636. User-defined accesors cannot use this code path because they may use custom logic that does not semantically perform a member access. Resolves SR-12639.
Add SR-12642 negative test: crash regarding `Differentiable` derived conformances and redeclared properties.
675f756
to
39f50dc
Compare
Add special-case VJP generation support for "semantic member accessors". This is necessary to avoid activity analysis related diagnostics and simplifies generated code. Fix "wrapped property mutability" check in `Differentiable` derived conformnances. This resolves SR-12642. Add e2e test using real world property wrappers (`@Lazy` and `@Clamping`).
39f50dc
to
96f3f6f
Compare
I pushed some final fixes to finish wrapped property differentiation:
End-to-end test
import _Differentiation
// Adapted from: https://github.com/apple/swift-evolution/blob/master/proposals/0258-property-wrappers.md#proposed-solution
@propertyWrapper
enum Lazy<Value> {
case uninitialized(() -> Value)
case initialized(Value)
init(wrappedValue: @autoclosure @escaping () -> Value) {
self = .uninitialized(wrappedValue)
}
var wrappedValue: Value {
// TODO(TF-1250): Replace with actual mutating getter implementation.
// Requires differentiation to support functions with multiple results.
get {
switch self {
case .uninitialized(let initializer):
let value = initializer()
return value
case .initialized(let value):
return value
}
}
set {
self = .initialized(newValue)
}
}
}
// From: https://github.com/apple/swift-evolution/blob/master/proposals/0258-property-wrappers.md#clamping-a-value-within-bounds
@propertyWrapper
struct Clamping<V: Comparable> {
var value: V
let min: V
let max: V
init(wrappedValue: V, min: V, max: V) {
value = wrappedValue
self.min = min
self.max = max
assert(value >= min && value <= max)
}
var wrappedValue: V {
get { return value }
set {
if newValue < min {
value = min
} else if newValue > max {
value = max
} else {
value = newValue
}
}
}
}
struct Struct: Differentiable {
@Lazy var x: Float = 3
@Clamping(min: -10, max: 10)
var y: Float = 4
}
@differentiable
func multiply(_ s: Struct) -> Float {
return s.x * s.y
}
print(gradient(at: Struct(x: 3, y: 4), in: multiply))
// Struct.TangentVector(x: 4.0, y: 3.0) This PR is ready to land now. |
@swift-ci Please test |
Differentiation now correctly supports stored properties with attached property wrappers.
Differentiable
derived conformancesDifferentiable
conformance derivation now "peers through" property wrappers.This behavior is always desirable.
Synthesized
TangentVector
structs contain wrapped properties'TangentVector
sas stored properties, not wrappers'
TangentVector
s.It is irrelevant whether property wrapper types conform to
Differentiable
.Property wrapper types are required to provide a setter for
var wrappedValue
,which is needed to synthesize
mutating func move(along:)
.Differentiation transform
The differentiation transform is also updated to support differentiation of
wrapped value accessors.
This is done via a new pullback generation code path for "semantic member
accessors". "Semantic member accessors" are accessors attached to member
properties that have a corresponding tangent stored property in the parent
TangentVector
type.These accessors have special-case pullback generation based on their semantic
behavior. Currently, only getters and setters are supported.
This special-case pullback generation is currently used for stored property
accessors and property wrapper wrapped value accessors. In the future, it can
also be used to support
@differentiable(useInTangentVector)
computedproperties: SR-12636.
User-defined accesors cannot use this code path because they may use custom
logic that does not semantically perform a member access.
Example
Resolves all of SR-12637 that can be supported today.