// RUN: %target-run-simple-swift // RUN: %target-swift-emit-sil -Xllvm -debug-only=differentiation -module-name null -o /dev/null 2>&1 %s | %FileCheck %s // REQUIRES: executable_test // REQUIRES: asserts // Test differentiation of `Optional` properties. import DifferentiationUnittest import StdlibUnittest var OptionalTests = TestSuite("OptionalPropertyDifferentiation") // Test `Optional` struct stored properties. struct Struct: Differentiable { var stored: Float var optional: Float? @differentiable(reverse) func method() -> Float { let s: Struct do { let tmp = Struct(stored: stored, optional: optional) let tuple = (tmp, tmp) s = tuple.0 } if let x = s.optional { return x * s.stored } return s.stored } } // Check active SIL instructions in representative original functions. // This tests SIL instruction coverage of derivative function cloners (e.g. PullbackCloner). // CHECK-LABEL: [AD] Activity info for ${{.*}}Struct{{.*}}method{{.*}} at parameter indices (0) and result indices (0) // CHECK: [ACTIVE] {{.*}} struct_extract {{%.*}} : $Struct, #Struct.stored // CHECK: [ACTIVE] {{.*}} struct_extract {{%.*}} : $Struct, #Struct.optional // CHECK: [ACTIVE] {{.*}} tuple ({{%.*}} : $Struct, {{%.*}} : $Struct) // CHECK: [ACTIVE] {{.*}} destructure_tuple {{%.*}} : $(Struct, Struct) // CHECK: [ACTIVE] {{.*}} struct_element_addr {{%.*}} : $*Struct, #Struct.optional // CHECK: [ACTIVE] {{.*}} struct_element_addr {{%.*}} : $*Struct, #Struct.stored // CHECK-LABEL: [AD] Activity info for $s4null6StructV6stored8optionalACSf_SfSgtcfC at parameter indices (0, 1) and result indices (0) // CHECK: [ACTIVE] {{%.*}} struct $Struct ({{%.*}} : $Float, {{%.*}} : $Optional) struct StructTracked: Differentiable { var stored: NonresilientTracked var optional: NonresilientTracked? @differentiable(reverse) func method() -> NonresilientTracked { let s: StructTracked do { let tmp = StructTracked(stored: stored, optional: optional) let tuple = (tmp, tmp) s = tuple.0 } if let x = s.optional { return x * s.stored } return s.stored } } struct StructGeneric: Differentiable { var stored: T var optional: T? @differentiable(reverse) func method() -> T { let s: StructGeneric do { let tmp = StructGeneric(stored: stored, optional: optional) let tuple = (tmp, tmp) s = tuple.0 } if let x = s.optional { return x } return s.stored } } OptionalTests.test("Optional struct stored properties") { expectEqual( valueWithGradient(at: Struct(stored: 3, optional: 4), of: { $0.method() }), (12, .init(stored: 4, optional: .init(3)))) expectEqual( valueWithGradient(at: Struct(stored: 3, optional: nil), of: { $0.method() }), (3, .init(stored: 1, optional: .init(0)))) expectEqual( valueWithGradient(at: StructTracked(stored: 3, optional: 4), of: { $0.method() }), (12, .init(stored: 4, optional: .init(3)))) expectEqual( valueWithGradient(at: StructTracked(stored: 3, optional: nil), of: { $0.method() }), (3, .init(stored: 1, optional: .init(0)))) expectEqual( valueWithGradient(at: StructGeneric(stored: 3, optional: 4), of: { $0.method() }), (4, .init(stored: 0, optional: .init(1)))) expectEqual( valueWithGradient(at: StructGeneric(stored: 3, optional: nil), of: { $0.method() }), (3, .init(stored: 1, optional: .init(0)))) } // Test `Optional` class stored properties. struct Class: Differentiable { var stored: Float var optional: Float? init(stored: Float, optional: Float?) { self.stored = stored self.optional = optional } @differentiable(reverse) func method() -> Float { let c: Class do { let tmp = Class(stored: stored, optional: optional) let tuple = (tmp, tmp) c = tuple.0 } if let x = c.optional { return x * c.stored } return c.stored } } struct ClassTracked: Differentiable { var stored: NonresilientTracked var optional: NonresilientTracked? init(stored: NonresilientTracked, optional: NonresilientTracked?) { self.stored = stored self.optional = optional } @differentiable(reverse) func method() -> NonresilientTracked { let c: ClassTracked do { let tmp = ClassTracked(stored: stored, optional: optional) let tuple = (tmp, tmp) c = tuple.0 } if let x = c.optional { return x * c.stored } return c.stored } } struct ClassGeneric: Differentiable { var stored: T var optional: T? init(stored: T, optional: T?) { self.stored = stored self.optional = optional } @differentiable(reverse) func method() -> T { let c: ClassGeneric do { let tmp = ClassGeneric(stored: stored, optional: optional) let tuple = (tmp, tmp) c = tuple.0 } if let x = c.optional { return x } return c.stored } } OptionalTests.test("Optional class stored properties") { expectEqual( valueWithGradient(at: Class(stored: 3, optional: 4), of: { $0.method() }), (12, .init(stored: 4, optional: .init(3)))) expectEqual( valueWithGradient(at: Class(stored: 3, optional: nil), of: { $0.method() }), (3, .init(stored: 1, optional: .init(0)))) expectEqual( valueWithGradient(at: ClassTracked(stored: 3, optional: 4), of: { $0.method() }), (12, .init(stored: 4, optional: .init(3)))) expectEqual( valueWithGradient(at: ClassTracked(stored: 3, optional: nil), of: { $0.method() }), (3, .init(stored: 1, optional: .init(0)))) expectEqual( valueWithGradient(at: ClassGeneric>(stored: 3, optional: 4), of: { $0.method() }), (4, .init(stored: 0, optional: .init(1)))) expectEqual( valueWithGradient(at: ClassGeneric>(stored: 3, optional: nil), of: { $0.method() }), (3, .init(stored: 1, optional: .init(0)))) } runAllTests()