https://github.com/EPFL-LGG/Cshells
Raw File
Tip revision: 77e2afd6b5dec26bddf79ff82d2ff5a1d2d62618 authored by qbecky on 19 March 2024, 14:38:04 UTC
Add representative images
Tip revision: 77e2afd
AverageAngleLinkage.cc
#include "AverageAngleLinkage.hh"

template<typename Real_>
void AverageAngleLinkage_T<Real_>::m_constructAverageAngleToJointAngleMapTranspose() {
    // std::cout<<"Samara construct change of variable matrix!"<<std::endl;
    // Each joint has a single angle variable.
    const SuiteSparse_long m = Base::numJoints();
    const SuiteSparse_long n = Base::numJoints();
    const SuiteSparse_long nja = m_actuatedAngles.size();
    SuiteSparseMatrix result(m, n);
    result.nz = m + 2 * (nja - 1);
    // Now we fill out the transpose of the map one column (each joint angle) at a time:
    // # Average Angle Vars [            ]
    //                       # All Angles

    auto &Ax = result.Ax;
    auto &Ai = result.Ai;
    auto &Ap = result.Ap;

    Ax.reserve(result.nz);
    Ai.reserve(result.nz);
    Ap.push_back(0);
    size_t prevActAngleIndex = NONE;
    for (size_t ai = 0; ai < (size_t)n; ++ai) {
        if (isActuatedAngle(ai) && prevActAngleIndex != NONE) {
            Ai.push_back(prevActAngleIndex);
            Ax.push_back(-1);
            prevActAngleIndex = ai;
        }
            Ai.push_back(ai  );
            Ax.push_back( 1);
        if (isActuatedAngle(ai) && ai < m_lastActuatedAngle) {
            Ai.push_back(m_lastActuatedAngle);
            Ax.push_back( 1);
            prevActAngleIndex = ai;
        }
        Ap.push_back(Ai.size()); // col end.
    }

    assert(Ai.size() == size_t(result.nz));
    assert(Ap.size() == size_t(n+1      ));
    m_averageAngleToJointAngleMapTranspose = std::move(result);
}

template<typename Real_>
void AverageAngleLinkage_T<Real_>::m_constructAverageAngleToJointAngleMapTranspose_AllJointVars() {
    // Each joint has a single angle variable.
    size_t jointOffset = Base::dofOffsetForJoint(0);
    size_t nj = Base::numJoints();
    const SuiteSparse_long m = Base::dofOffsetForJoint(nj - 1) + Base::joint(nj - 1).numDoF() - jointOffset; // total number of joints DoFs
    const SuiteSparse_long n = m;
    const SuiteSparse_long nja = m_actuatedAngles.size();
    SuiteSparseMatrix result(m, n);
    result.nz = m + 2 * (nja - 1);
    // Now we fill out the transpose of the map one column (each joint angle) at a time:
    // # Average Angle Vars [            ]
    //                       # All Angles

    auto &Ax = result.Ax;
    auto &Ai = result.Ai;
    auto &Ap = result.Ap;

    Ax.reserve(result.nz);
    Ai.reserve(result.nz);
    Ap.push_back(0);
    size_t prevActAngleIndex = NONE;
    for (size_t ji = 0; ji < (size_t)n; ++ji) {
        if (isActuatedAngleVar(ji + jointOffset) && prevActAngleIndex != NONE) {
            size_t ai = getAngleIndexFromDoFIndex(ji + jointOffset);
            Ai.push_back(getDoFIndexFromAngleIndex(prevActAngleIndex) - jointOffset);
            Ax.push_back(-1);
            prevActAngleIndex = ai;
        }
        Ai.push_back(ji  );
        Ax.push_back( 1);
        if (isActuatedAngleVar(ji + jointOffset)) {
            size_t ai = getAngleIndexFromDoFIndex(ji + jointOffset);
            if (ai < m_lastActuatedAngle) {
                Ai.push_back(getDoFIndexFromAngleIndex(m_lastActuatedAngle) - jointOffset);
                Ax.push_back( 1);
                prevActAngleIndex = ai;
            }
        }
        Ap.push_back(Ai.size()); // col end.
    }

    assert(Ai.size() == size_t(result.nz));
    assert(Ap.size() == size_t(n+1      ));
    m_averageAngleToJointAngleMapTranspose_AllJointVars = std::move(result);
}

template<typename Real_>
void AverageAngleLinkage_T<Real_>::m_constructActuatedAngles(){
    if (m_freeAngles.size() == Base::numJoints()) throw std::runtime_error("Too many joints are left free.");
    m_actuatedAngles.clear();
    m_firstActuatedAngle = Base::numJoints();
    m_lastActuatedAngle = 0;
    for (size_t ji = 0; ji < Base::numJoints(); ji++){
        if (isActuatedAngle(ji)){
            m_actuatedAngles.push_back(ji);
            if (ji != NONE && ji < m_firstActuatedAngle) { m_firstActuatedAngle = ji; }
            if (ji != NONE && ji > m_lastActuatedAngle) { m_lastActuatedAngle = ji; }
        }
    }
    m_averageAngleIndex = getDoFIndexFromAngleIndex(m_lastActuatedAngle);
}


template<typename Real_>
void AverageAngleLinkage_T<Real_>::m_buildAngleIndexForDoFIndex() {
    m_angleIndexFromDoFIndex.resize(Base::numDoF());
    for (size_t i = 0; i < Base::numDoF(); ++i) m_angleIndexFromDoFIndex[i] = NONE;
    for (size_t ji = 0; ji < Base::numJoints(); ++ji) m_angleIndexFromDoFIndex[Base::dofOffsetForJoint(ji) + 6] = ji;
}

template<typename Real_>
size_t AverageAngleLinkage_T<Real_>::hessianNNZ(bool variableDesignParameters) const {
    size_t result = Base::hessianNNZ();
    // for (size_t ji = 0; ji < Base::numJoints() - 1; ++ji) {
    for (size_t ji = 0; ji < m_actuatedAngles.size() - 1; ++ji) {
        auto & j = Base::joint(m_actuatedAngles[ji]);
        auto & next_j = Base::joint(m_actuatedAngles[ji+1]);
        // The last average angle variable interacts with every other joint variables 
        // as well as the incident segments end edge variables that those joints control. 
        // The contribution of the last joint's average angle variable's interaction with its own variables are already counted. 
        result += j.numDoF() + j.valence() * 2 * (3 + 1); // per valence: 2 overlapping edges with 3 (position) + 1 (frame angle) DoFs
        // Every joint's angle variable interacts with the variables of the next joints as well as the incident segments end edge variables that those joints control. 
        result += next_j.numDoF() + next_j.valence() * 2 * (3 + 1);

        if (variableDesignParameters) {            
            throw std::runtime_error("variableDesignParameters is not implemented in hessianNNZ of AverageAngleLinkage!");
        }
    }
    // The contribution of the interaction between the last two angle variables is overcounted. 
    result -= 1;

    return result;
}

template<typename Real_>
auto AverageAngleLinkage_T<Real_>::hessianSparsityPattern(bool variableDesignParameters, Real_ val) const -> CSCMat {
    if (m_cachedHessianSparsity) return *m_cachedHessianSparsity;
    TMatrix result(Base::numDoF(), Base::numDoF()); 
    result.symmetry_mode = TMatrix::SymmetryMode::UPPER_TRIANGLE;

    if (variableDesignParameters) { throw std::runtime_error("AAL hsp is not implemented for design parameters yet!"); }
    auto baseHSP = Base::hessianSparsityPattern(variableDesignParameters, 0.0);
    for (const auto t : baseHSP) { result.addNZ(t.i, t.j, 1.0); }

    // Add new entries representing the coupling between the transformed angle variables. 
    // for (size_t ji = 0; ji < Base::numJoints() - 1; ++ji) {
    for (size_t ji = 0; ji < m_actuatedAngles.size() - 1; ++ji) {
        const auto &j = Base::joint(m_actuatedAngles[ji]);
        const size_t jo = Base::dofOffsetForJoint(m_actuatedAngles[ji]);
        const auto &next_j = Base::joint(m_actuatedAngles[ji + 1]);
        const size_t next_jo = Base::dofOffsetForJoint(m_actuatedAngles[ji + 1]);

        // Add coupling between the current joint angle variable and the next joint's all variables + its segments vars. 
        for (size_t k = 0; k < next_j.numDoF(); ++k) result.addNZ(jo + 6, next_jo + k, 1.0);
        auto addIdx = [&](const size_t idx) { result.addNZ(idx, jo + 6, 1.0); };
        next_j.visitInfluencedSegmentVars(6, addIdx);

        // Add coupling between the average angle variable (stored in the last joint) and the current joint's all variables + its segments vars. 
        for (size_t k = 0; k < j.numDoF(); ++k) result.addNZ(jo + k, m_averageAngleIndex, 1.0);

        auto addSecondIdx = [&](const size_t idx) { result.addNZ(idx, m_averageAngleIndex, 1.0); };
        j.visitInfluencedSegmentVars(6, addSecondIdx);

    }

    if (variableDesignParameters) { 
        throw std::runtime_error("AAL hsp is not implemented for design parameters yet!");
        // m_cachedHessianVarRLSparsity = std::make_unique<CSCMat>(result); 
        // m_cachedHessianVarRLSparsity ->fill(val);
        // return *m_cachedHessianVarRLSparsity;
    } else { 
        m_cachedHessianSparsity      = std::make_unique<CSCMat>(result); 
        m_cachedHessianSparsity      ->fill(val);
        if (size_t(m_cachedHessianSparsity->nz) != hessianNNZ()) throw std::runtime_error("Incorrect NNZ prediction: " + std::to_string(m_cachedHessianSparsity->nz) + " vs " + std::to_string(hessianNNZ()));

        return *m_cachedHessianSparsity;
    }

    return *m_cachedHessianSparsity;
}



template<typename Real_>
void AverageAngleLinkage_T<Real_>::hessian(CSCMat &H, EnergyType eType, const bool variableDesignParameters) const {
    assert(H.symmetry_mode == CSCMat::SymmetryMode::UPPER_TRIANGLE);
    BENCHMARK_SCOPED_TIMER_SECTION timer(mangledName() + ".hessian");
    if (variableDesignParameters) throw std::runtime_error("AvergaeAngleLinkage hessian is not implemented for variableDesignParameters!");
    // Compute Hessian using per-edge rest lengths
    auto baseH = Base::hessianSparsityPattern(variableDesignParameters);
    Base::hessian(baseH, eType, variableDesignParameters);
    const SuiteSparseMatrix &A = m_averageAngleToJointAngleMapTranspose_AllJointVars;

    std::vector<size_t> angleDoFIndices = Base::jointAngleDoFIndices();
    const size_t nj = Base::numJoints();
    const size_t jointOffset = Base::dofOffsetForJoint(0);
    const size_t njv = Base::dofOffsetForJoint(nj - 1) + Base::joint(nj - 1).numDoF() - jointOffset;

    // Copy the rods hessian over. 
    H.addWithSubSparsity(baseH, /* scale */ 1.0, /*  idx offset */ 0, /* block start */ 0, /* block end */ jointOffset);

    size_t hint = 0;
    for (size_t ji = 0; ji < njv; ++ji) {
        const size_t j = ji + jointOffset;

        // Loop over each output column "l" generated by angle variable "j"
        const size_t lend = A.Ap[ji + 1];
        for (size_t idx = A.Ap[ji]; idx < lend; ++idx) {
            const size_t l = A.Ai[idx] + jointOffset;
            const Real_ colMultiplier = A.Ax[idx];

            // Create entries for each input Hessian entry
            const size_t input_end = baseH.Ap[j + 1];
            for (size_t idx_in = baseH.Ap[j]; idx_in < input_end; ++idx_in) {
                const Real_ colVal = colMultiplier * baseH.Ax[idx_in];
                const size_t i = baseH.Ai[idx_in];
                if (i < jointOffset) { // left transformation is in the identity block
                    hint = H.addNZ(i, l, colVal, hint);
                }
                else {
                    // Loop over each output entry
                    const size_t jj = i - jointOffset;
                    size_t kprev = 0;
                    size_t kprev_idx = 0;
                    const size_t outrow_end = A.Ap[jj + 1];
                    for (size_t outrow_idx = A.Ap[jj]; outrow_idx < outrow_end; ++outrow_idx) {
                        const size_t k = A.Ai[outrow_idx] + jointOffset;
                        const Real_ val = A.Ax[outrow_idx] * colVal;
                        if (k <= l) {
                            // Accumulate entries from input's upper triangle
                            if (k == kprev) { H.addNZ(kprev_idx, val); }
                            else     { hint = H.addNZ(k, l, val, hint);
                                       kprev = k, kprev_idx = hint - 1; }
                        }
                        if ((i != j) && (l <= k)) H.addNZ(l, k, val); // accumulate entries from input's (strict) lower triangle
                    }
                }
            }
        }
    }

}

template<typename Real_>
void AverageAngleLinkage_T<Real_>::massMatrix(CSCMat &M, bool updatedSource, bool useLumped) const {
    BENCHMARK_SCOPED_TIMER_SECTION timer(mangledName() + ".massMatrix");
    assert(M.symmetry_mode == CSCMat::SymmetryMode::UPPER_TRIANGLE);

    // Compute Hessian using per-edge rest lengths
    auto baseM = Base::hessianSparsityPattern();
    Base::massMatrix(baseM, updatedSource, useLumped);
    const SuiteSparseMatrix &A = m_averageAngleToJointAngleMapTranspose_AllJointVars;

    std::vector<size_t> angleDoFIndices = Base::jointAngleDoFIndices();
    const size_t nj = Base::numJoints();
    const size_t jointOffset = Base::dofOffsetForJoint(0);
    const size_t njv = Base::dofOffsetForJoint(nj - 1) + Base::joint(nj - 1).numDoF() - jointOffset;

    // Copy the RodLinkage massMatrix over. 
    M.addWithSubSparsity(baseM, /* scale */ 1.0, /*  idx offset */ 0, /* block start */ 0, /* block end */ jointOffset);

    size_t hint = 0;
    for (size_t ji = 0; ji < njv; ++ji) {
        const size_t j = ji + jointOffset;

        // Loop over each output column "l" generated by angle variable "j"
        const size_t lend = A.Ap[ji + 1];
        for (size_t idx = A.Ap[ji]; idx < lend; ++idx) {
            const size_t l = A.Ai[idx] + jointOffset;
            const Real_ colMultiplier = A.Ax[idx];

            // Create entries for each input Hessian entry
            const size_t input_end = baseM.Ap[j + 1];
            for (size_t idx_in = baseM.Ap[j]; idx_in < input_end; ++idx_in) {
                const Real_ colVal = colMultiplier * baseM.Ax[idx_in];
                const size_t i = baseM.Ai[idx_in];
                if (i < jointOffset) { // left transformation is in the identity block
                    hint = M.addNZ(i, l, colVal, hint);
                }
                else {
                    // Loop over each output entry
                    const size_t jj = i - jointOffset;
                    size_t kprev = 0;
                    size_t kprev_idx = 0;
                    const size_t outrow_end = A.Ap[jj + 1];
                    for (size_t outrow_idx = A.Ap[jj]; outrow_idx < outrow_end; ++outrow_idx) {
                        const size_t k = A.Ai[outrow_idx] + jointOffset;
                        const Real_ val = A.Ax[outrow_idx] * colVal;
                        if (k <= l) {
                            // Accumulate entries from input's upper triangle
                            if (k == kprev) { M.addNZ(kprev_idx, val); }
                            else     { hint = M.addNZ(k, l, val, hint);
                                       kprev = k, kprev_idx = hint - 1; }
                        }
                        if ((i != j) && (l <= k)) M.addNZ(l, k, val); // accumulate entries from input's (strict) lower triangle
                    }
                }
            }
        }
    }

}


////////////////////////////////////////////////////////////////////////////////
// Explicit instantiation for ordinary double type and autodiff type.
////////////////////////////////////////////////////////////////////////////////
template struct AverageAngleLinkage_T<Real>;
template struct AverageAngleLinkage_T<ADReal>;
back to top