Skip to content

Commit a1f9b9d

Browse files
PetroZarytskyivgvassilev
authored andcommitted
Do not treat tensors in a special way
Copy-initialization of adjoints has proven to be more reliable than default initialization. However, when handling non-differentiable parameters (unlike other variables), we use this strategy only with tensor-like types. This PR generalizes this approach to all copiable record types. Fixes #1526
1 parent 61798b4 commit a1f9b9d

3 files changed

Lines changed: 13 additions & 69 deletions

File tree

include/clad/Differentiator/CladUtils.h

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -417,10 +417,6 @@ namespace clad {
417417

418418
bool IsDifferentiableType(clang::QualType T);
419419

420-
/// Returns true if T is a Tensor-like type. This type must be
421-
/// forward-declared in the `clad::tensor_like` namespace.
422-
bool isTensorLike(clang::Sema& SemaRef, clang::QualType T);
423-
424420
bool hasElidableReverseForwAttribute(const clang::Decl* D);
425421

426422
/// Returns true if FD can be differentiated as a pushforward

lib/Differentiator/CladUtils.cpp

Lines changed: 0 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -1621,70 +1621,6 @@ namespace clad {
16211621
return false;
16221622
}
16231623

1624-
bool isTensorLike(clang::Sema& SemaRef, clang::QualType T) {
1625-
if (!isa<TemplateSpecializationType>(T) && !T->isRecordType())
1626-
return false;
1627-
// We check if the type is a vector, and if so, recursively check its
1628-
// template argument.
1629-
auto isVector = [](const TemplateSpecializationType* TST) {
1630-
TemplateName TM = TST->getTemplateName();
1631-
TemplateDecl* TD = TM.getAsTemplateDecl();
1632-
if (!TD)
1633-
return false;
1634-
return TD->getName() == "vector";
1635-
};
1636-
1637-
if (const auto* TST = T->getAs<TemplateSpecializationType>()) {
1638-
if (isVector(TST)) {
1639-
auto args = TST->template_arguments();
1640-
if (!args.empty()) {
1641-
// Recursively check the first template argument.
1642-
const clang::TemplateArgument& Arg = args[0];
1643-
if (Arg.getKind() == clang::TemplateArgument::Type)
1644-
return isTensorLike(SemaRef, Arg.getAsType());
1645-
}
1646-
}
1647-
}
1648-
1649-
const auto* CXXRD = T->getAsCXXRecordDecl();
1650-
// Find the special `tensor_like` namespace.
1651-
// This looks for `clad::tensor_like`.
1652-
clang::NamespaceDecl* CladNS =
1653-
utils::LookupNSD(SemaRef, "clad", /*shouldExist=*/true);
1654-
clang::NamespaceDecl* TensorLikeNS = utils::LookupNSD(
1655-
SemaRef, "tensor_like", /*shouldExist=*/false, CladNS);
1656-
if (!TensorLikeNS)
1657-
return false;
1658-
1659-
// Get the original type's name and its enclosing namespace.
1660-
clang::IdentifierInfo* TypeName = CXXRD->getIdentifier();
1661-
if (!TypeName)
1662-
return false;
1663-
1664-
const auto* OriginalDC = CXXRD->getDeclContext();
1665-
// The type is not in a namespace (e.g., it's a nested class or in
1666-
// global scope). You could extend this logic if needed, but for now,
1667-
// we'll assume tensor types are in a namespace like `at` or `cladtorch`.
1668-
if (!OriginalDC->isNamespace())
1669-
return false;
1670-
const auto* OriginalNS = clang::cast<clang::NamespaceDecl>(OriginalDC);
1671-
clang::IdentifierInfo* OriginalNSName = OriginalNS->getIdentifier();
1672-
if (!OriginalNSName)
1673-
return false;
1674-
1675-
clang::NamespaceDecl* FoundMirroredNS =
1676-
utils::LookupNSD(SemaRef, OriginalNSName->getName(),
1677-
/*shouldExist=*/false, TensorLikeNS);
1678-
if (!FoundMirroredNS)
1679-
return false;
1680-
1681-
clang::LookupResult R(SemaRef, clang::DeclarationName(TypeName),
1682-
clang::SourceLocation(),
1683-
clang::Sema::LookupOrdinaryName);
1684-
1685-
return SemaRef.LookupQualifiedName(R, FoundMirroredNS) && !R.empty();
1686-
}
1687-
16881624
/// Called in ShouldRecompute. In CUDA, to access a current thread/block id
16891625
/// we use functions that do not change the state of any variable, since no
16901626
/// point to store the value.

lib/Differentiator/ReverseModeVisitor.cpp

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -408,8 +408,20 @@ Expr* ReverseModeVisitor::getStdInitListSizeExpr(const Expr* E) {
408408
auto VDDerivedType = utils::getNonConstType(paramTy, m_Sema);
409409
VDDerivedType = VDDerivedType.getNonReferenceType();
410410
Expr* initExpr = nullptr;
411+
// We initialize adjoints with original variables as part of
412+
// the strategy to maintain the structure of the original variable.
413+
// After that, we'll zero-initialize the adjoint. e.g.
414+
// ```
415+
// std::vector<...> v{x, y, z};
416+
// std::vector<...> _d_v{v}; // The length of the vector is preserved
417+
// clad::zero_init(_d_v);
418+
// ```
419+
// Also, if the original is initialized with a zero-constructor, it can
420+
// be used for the adjoint as well.
421+
const CXXRecordDecl* RD = VDDerivedType->getAsCXXRecordDecl();
422+
bool isNonAggrClass = RD && !RD->isAggregate();
411423
bool isDirectInit = false;
412-
if (clad::utils::isTensorLike(m_Sema, VDDerivedType)) {
424+
if (isNonAggrClass && utils::isCopyable(RD)) {
413425
ParmVarDecl* newFuncParam = nullptr;
414426
for (auto* p : m_Derivative->parameters()) {
415427
if (p->getName() == param->getName()) {

0 commit comments

Comments
 (0)