bool link_sparse_hessian( size_t size , size_t repeat , CppAD::vector<double>& x , const CppAD::vector<size_t>& row , const CppAD::vector<size_t>& col , CppAD::vector<double>& hessian ) { // ----------------------------------------------------- // setup typedef vector<double> DblVector; typedef vector< std::set<size_t> > SetVector; typedef CppAD::AD<double> ADScalar; typedef vector<ADScalar> ADVector; size_t i, j, k; size_t order = 0; // derivative order corresponding to function size_t m = 1; // number of dependent variables size_t n = size; // number of independent variables size_t K = row.size(); // number of non-zeros in lower triangle ADVector a_x(n); // AD domain space vector ADVector a_y(m); // AD range space vector DblVector w(m); // double range space vector DblVector hes(K); // non-zeros in lower triangle CppAD::ADFun<double> f; // AD function object // weights for hessian calculation (only one component of f) w[0] = 1.; // use the unspecified fact that size is non-decreasing between calls static size_t previous_size = 0; bool print = (repeat > 1) & (previous_size != size); previous_size = size; // declare sparsity pattern # if USE_SET_SPARSITY SetVector sparsity(n); # else typedef vector<bool> BoolVector; BoolVector sparsity(n * n); # endif // initialize all entries as zero for(i = 0; i < n; i++) { for(j = 0; j < n; j++) hessian[ i * n + j] = 0.; } // ------------------------------------------------------ extern bool global_retape; if( global_retape) while(repeat--) { // choose a value for x CppAD::uniform_01(n, x); for(j = 0; j < n; j++) a_x[j] = x[j]; // declare independent variables Independent(a_x); // AD computation of f(x) CppAD::sparse_hes_fun<ADScalar>(n, a_x, row, col, order, a_y); // create function object f : X -> Y f.Dependent(a_x, a_y); extern bool global_optimize; if( global_optimize ) { print_optimize(f, print, "cppad_sparse_hessian_optimize", size); print = false; } // calculate the Hessian sparsity pattern for this function calc_sparsity(sparsity, f); // structure that holds some of work done by SparseHessian CppAD::sparse_hessian_work work; // calculate this Hessian at this x f.SparseHessian(x, w, sparsity, row, col, hes, work); for(k = 0; k < K; k++) { hessian[ row[k] * n + col[k] ] = hes[k]; hessian[ col[k] * n + row[k] ] = hes[k]; } } else { // choose a value for x CppAD::uniform_01(n, x); for(j = 0; j < n; j++) a_x[j] = x[j]; // declare independent variables Independent(a_x); // AD computation of f(x) CppAD::sparse_hes_fun<ADScalar>(n, a_x, row, col, order, a_y); // create function object f : X -> Y f.Dependent(a_x, a_y); extern bool global_optimize; if( global_optimize ) { print_optimize(f, print, "cppad_sparse_hessian_optimize", size); print = false; } // calculate the Hessian sparsity pattern for this function calc_sparsity(sparsity, f); // declare structure that holds some of work done by SparseHessian CppAD::sparse_hessian_work work; while(repeat--) { // choose a value for x CppAD::uniform_01(n, x); // calculate sparsity at this x f.SparseHessian(x, w, sparsity, row, col, hes, work); for(k = 0; k < K; k++) { hessian[ row[k] * n + col[k] ] = hes[k]; hessian[ col[k] * n + row[k] ] = hes[k]; } } } return true; }
DataType& SparseStorage<DataType>::elem(casadi_int rr, casadi_int cc) { casadi_int oldsize = sparsity().nnz(); casadi_int ind = sparsity_.add_nz(rr, cc); if (oldsize != sparsity().nnz()) nonzeros().insert(nonzeros().begin()+ind, DataType(0)); return nonzeros().at(ind); }
DataType& SparseStorage<DataType>::elem(int rr, int cc) { int oldsize = sparsity().size(); int ind = sparsityRef().getNZ(rr, cc); if (oldsize != sparsity().size()) data().insert(begin()+ind, DataType(0)); return at(ind); }
void Multiplication::generateOperation(std::ostream &stream, const std::vector<std::string>& arg, const std::vector<std::string>& res, CodeGenerator& gen) const{ // Clear the result stream << " casadi_fill(" << sparsity().size() << ",0.0," << res.front() << ",1);" << endl; // Perform sparse matrix multiplication stream << " casadi_mm_nt_sparse("; for(int i=0; i<2; ++i){ stream << arg.at(i) << ",s" << gen.getSparsity(dep(i).sparsity()) << ","; } stream << res.front() << ",s" << gen.getSparsity(sparsity()) << ");" << endl; }
std::string Reshape::print(const std::vector<std::string>& arg) const { // For vectors, reshape is also a transpose if (dep().is_vector() && sparsity().is_vector()) { // Print as transpose: X' return arg.at(0) + "'"; } else { // Print as reshape(X) or vec(X) if (sparsity().is_column()) { return "vec(" + arg.at(0) + ")"; } else { return "reshape(" + arg.at(0) + ")"; } } }
const DataType& SparseStorage<DataType>::elem(int rr, int cc) const { int ind = sparsity().getNZ(rr, cc); if (ind==-1) return casadi_limits<DataType>::zero; else return at(ind); }
void Project::evalFwd(const std::vector<std::vector<MX> >& fseed, std::vector<std::vector<MX> >& fsens) { int nfwd = fsens.size(); for (int d=0; d<nfwd; ++d) { fsens[d][0] = project(fseed[d][0], sparsity(), true); } }
void HorzRepsum::spAdj(bvec_t** arg, bvec_t** res, int* iw, bvec_t* w, int mem) { int nnz = sparsity().nnz(); for (int i=0;i<n_;++i) { std::transform(res[0], res[0]+nnz, arg[0]+i*nnz, arg[0]+i*nnz, &Orring); } std::fill(res[0], res[0]+nnz, 0); }
std::string Project::print(const std::vector<std::string>& arg) const { if (sparsity().isdense()) { return "dense(" + arg.at(0) + ")"; } else { return "project(" + arg.at(0) + ")"; } }
MX Reshape::getTranspose() const { // For vectors, reshape is also a transpose if (dep().is_vector() && sparsity().is_vector()) { return dep(); } else { return MXNode::getTranspose(); } }
void UnaryMX::generateOperation(std::ostream &stream, const std::vector<std::string>& arg, const std::vector<std::string>& res, CodeGenerator& gen) const{ stream << " for(i=0; i<" << sparsity().size() << "; ++i) "; stream << res.at(0) << "[i]="; casadi_math<double>::printPre(op_,stream); stream << arg.at(0) << "[i]"; casadi_math<double>::printPost(op_,stream); stream << ";" << endl; }
void Transpose::generate(const std::vector<int>& arg, const std::vector<int>& res, CodeGenerator& g) const { g.addAuxiliary(CodeGenerator::AUX_TRANS); g.body << " trans(" << g.work(arg[0], nnz()) << ", " << g.sparsity(dep().sparsity()) << ", " << g.work(res[0], nnz()) << ", " << g.sparsity(sparsity()) << ", iw);" << endl; }
void HorzRepsum::evalGen(const T** arg, T** res, int* iw, T* w, int mem, R reduction) const { int nnz = sparsity().nnz(); fill_n(res[0], nnz, 0); for (int i=0;i<n_;++i) { std::transform(arg[0]+i*nnz, arg[0]+(i+1)*nnz, res[0], res[0], reduction); } }
void Transpose::spFwd(const bvec_t** arg, bvec_t** res, int* iw, bvec_t* w) { // Shortands const bvec_t *x = arg[0]; bvec_t *xT = res[0]; // Get sparsity int nz = nnz(); const int* x_row = dep().row(); const int* xT_colind = sparsity().colind(); int xT_ncol = sparsity().size2(); // Loop over the nonzeros of the argument copy(xT_colind, xT_colind+xT_ncol+1, iw); for (int el=0; el<nz; ++el) { xT[iw[*x_row++]++] = *x++; } }
void Transpose::evalGen(const T* const* arg, T* const* res, int* iw, T* w) { // Get sparsity patterns //const vector<int>& x_colind = input[0]->colind(); const int* x_row = dep(0).row(); int x_sz = dep(0).nnz(); const int* xT_colind = sparsity().colind(); int xT_ncol = sparsity().size2(); const T* x = arg[0]; T* xT = res[0]; // Transpose copy(xT_colind, xT_colind+xT_ncol+1, iw); for (int el=0; el<x_sz; ++el) { xT[iw[x_row[el]]++] = x[el]; } }
void HorzRepmat::generate(CodeGenerator& g, const std::string& mem, const std::vector<int>& arg, const std::vector<int>& res) const { int nnz = dep(0).nnz(); g.body << " for (i=0;i<" << n_ << ";++i) {" << endl; g.body << " " << g.copy(g.work(arg[0], dep(0).nnz()), nnz, g.work(res[0], sparsity().nnz()) + "+ i*" + g.to_string(nnz)) << endl << " }" << endl; }
void HorzRepsum::generate(CodeGenerator& g, const std::string& mem, const std::vector<int>& arg, const std::vector<int>& res) const { int nnz = sparsity().nnz(); g.body << " " << g.fill(g.work(res[0], nnz), nnz, "0") << endl; g.body << " for (i=0;i<" << n_ << ";++i) {" << endl; g.body << " for (j=0;j<" << nnz << ";++j) {" << endl; g.body << " " << g.work(res[0], nnz)<< "[j] += " << g.work(arg[0], dep(0).nnz()) << "[j+i*" << nnz << "];" << endl; g.body << " }" << endl; g.body << " }" << endl; }
void Split::spFwd(const bvec_t** arg, bvec_t** res, int* iw, bvec_t* w, int mem) { int nx = offset_.size()-1; for (int i=0; i<nx; ++i) { if (res[i]!=0) { const bvec_t *arg_ptr = arg[0] + offset_[i]; int n_i = sparsity(i).nnz(); bvec_t *res_i_ptr = res[i]; for (int k=0; k<n_i; ++k) { *res_i_ptr++ = *arg_ptr++; } } } }
bool GetNonzerosSlice::isIdentity() const { // Check sparsity if (!(sparsity() == dep().sparsity())) return false; // Check if the nonzeros follow in increasing order if (s_.start_ != 0) return false; if (s_.step_ != 1) return false; if (s_.stop_ != size()) return false; // True if reached this point return true; }
void Split::spAdj(const pv_bvec_t& arg, const pv_bvec_t& res, int* itmp, bvec_t* rtmp) { int nx = offset_.size()-1; for (int i=0; i<nx; ++i) { if (res[i]!=0) { bvec_t *arg_ptr = arg[0] + offset_[i]; int n_i = sparsity(i).nnz(); bvec_t *res_i_ptr = res[i]; for (int k=0; k<n_i; ++k) { *arg_ptr++ |= *res_i_ptr; *res_i_ptr++ = 0; } } } }
void Multiplication<TrX,TrY>::generateOperation(std::ostream &stream, const std::vector<std::string>& arg, const std::vector<std::string>& res, CodeGenerator& gen) const{ // Check if inplace bool inplace = arg.at(0).compare(res.front())==0; // Copy first argument if not inplace if(!inplace){ stream << " for(i=0; i<" << this->size() << "; ++i) " << res.front() << "[i]=" << arg.at(0) << "[i];" << endl; } // Perform sparse matrix multiplication gen.addAuxiliary(CodeGenerator::AUX_MM_TN_SPARSE); stream << " casadi_mm_tn_sparse("; stream << arg.at(1) << ",s" << gen.getSparsity(dep(1).sparsity()) << ","; stream << arg.at(2) << ",s" << gen.getSparsity(dep(2).sparsity()) << ","; stream << res.front() << ",s" << gen.getSparsity(sparsity()) << ");" << endl; }
int main(int argc, char *argv[]) { // tests(); // srand(time(10)); srand(10); if(argc < 4) { std::cout << "Input arguments:\n\t1: Filename for A matrix (as in A ~= WH)\n\t2: New desired dimension\n\t3: Max NMF iterations\n\t4: Max NNLS iterations\n\t5 (optional): delimiter (space is default)\n"; } else { std::string filename = argv[1]; int newDimension = atoi(argv[2]); int max_iter_nmf = atoi(argv[3]); int max_iter_nnls = atoi(argv[4]); char delimiter = (argc > 5) ? *argv[5] : ' '; DenseMatrix* A = readMatrix(filename,delimiter); A->copyColumnToRow(); printf("Sparsity of A: %f\n",sparsity(A)); DenseMatrix W = DenseMatrix(A->rows,newDimension); DenseMatrix H = DenseMatrix(newDimension,A->cols); NMF_Input input = NMF_Input(&W,&H,A,max_iter_nmf,max_iter_nnls); std::cout << "Starting NMF computation." << std::endl; std::clock_t start = std::clock(); double duration; // nmf_cpu(input); nmf_cpu_profile(input); duration = ( std::clock() - start ) / (double) CLOCKS_PER_SEC; std::cout << "NMF computation complete. Time: " << duration << " s." << std::endl; W.copyColumnToRow(); dtype AF = FrobeniusNorm(A); dtype WH_AF = Fnorm(W,H,*A); printf("Objective value: %f\n",WH_AF/AF); // DenseMatrix z1 = DenseMatrix(A->rows,newDimension); // DenseMatrix z2 = DenseMatrix(newDimension,A->cols); // printf("Calculated solution approximate Frobenius norm: %f\n",Fnorm(z1,z2,*A)); // printcolmajor(H.colmajor,H.rows,H.cols); if(A) delete A; } return 0; }
void EvaluationMX::generateOperation(std::ostream &stream, const std::vector<std::string>& arg, const std::vector<std::string>& res, CodeGenerator& gen) const{ // Get the index of the function int f = gen.getDependency(fcn_); stream << " f" << f << "_buffered("; // Pass inputs to the function input buffers for(int i=0; i<arg.size(); ++i){ // Pass argument to the function stream << arg.at(i) << ","; // Pass argument sparsity to the function if(dep(i).isNull()){ stream << "0"; } else { int sp_i = gen.getSparsity(dep(i).sparsity()); stream << "s" << sp_i; } // Separate with a space to visualize argument grouping if(i+1<arg.size()+res.size()) stream << ", "; } // Separate arguments and results with two extra spaces stream << " "; // Pass results to the function input buffers for(int i=0; i<res.size(); ++i){ // Pass results buffer to the function stream << res.at(i) << ","; // Pass argument sparsity to the function int sp_i = gen.getSparsity(sparsity(i)); stream << "s" << sp_i; // Separate with a space to visualize argument grouping if(i+1<res.size()) stream << ", "; } // Finalize the function call stream << ");" << endl; }
void Project::spAdj(bvec_t** arg, bvec_t** res, int* iw, bvec_t* w) { dep().sparsity().bor(arg[0], res[0], sparsity()); fill(res[0], res[0]+nnz(), 0); }
void Project::generate(const std::vector<int>& arg, const std::vector<int>& res, CodeGenerator& g) const { g.body << " " << g.project(g.work(arg.front(), dep().nnz()), dep(0).sparsity(), g.work(res.front(), nnz()), sparsity(), "w") << endl; }
void SetNonzeros<Add>::eval_mx(const std::vector<MX>& arg, std::vector<MX>& res) { // Get all the nonzeros vector<int> nz = all(); // Output sparsity const Sparsity &osp = sparsity(); const int* orow = osp.row(); vector<int> ocol = osp.get_col(); // Input sparsity (first input same as output) const Sparsity &isp = dep(1).sparsity(); vector<int> icol = isp.get_col(); // We next need to resort the assignment vector by outputs instead of inputs // Start by counting the number of output nonzeros corresponding to each input nonzero vector<int> onz_count(osp.nnz()+2, 0); for (vector<int>::const_iterator it=nz.begin(); it!=nz.end(); ++it) { onz_count[*it+2]++; } // Cumsum to get index offset for output nonzero for (int i=0; i<onz_count.size()-1; ++i) { onz_count[i+1] += onz_count[i]; } // Get the order of assignments vector<int> nz_order(nz.size()); for (int k=0; k<nz.size(); ++k) { // Save the new index nz_order[onz_count[1+nz[k]]++] = k; } // Find out which elements are being set vector<int>& with_duplicates = onz_count; // Reuse memory onz_count.resize(nz.size()); for (int k=0; k<nz.size(); ++k) { // Get output nonzero int onz_k = nz[nz_order[k]]; // Get element (note: may contain duplicates) if (onz_k>=0) { with_duplicates[k] = ocol[onz_k]*osp.size1() + orow[onz_k]; } else { with_duplicates[k] = -1; } } // Get all output elements (this time without duplicates) vector<int> el_output; osp.find(el_output); // Sparsity pattern being formed and corresponding nonzero mapping vector<int> r_colind, r_row, r_nz, r_ind; // Get references to arguments and results res[0] = arg[0]; // Entries in res with elements zero'ed out if (!Add) { // Get the nz locations in res corresponding to the output sparsity pattern r_nz.resize(with_duplicates.size()); copy(with_duplicates.begin(), with_duplicates.end(), r_nz.begin()); res[0].sparsity().get_nz(r_nz); // Zero out the corresponding entries res[0] = MX::zeros(isp)->getSetNonzeros(res[0], r_nz); } // Get the nz locations of the elements in arg corresponding to the argument sparsity pattern arg[1].sparsity().find(r_nz); isp.get_nz(r_nz); // Filter out ignored entries and check if there is anything to add at all bool elements_to_add = false; for (vector<int>::iterator k=r_nz.begin(); k!=r_nz.end(); ++k) { if (*k>=0) { if (nz[*k]>=0) { elements_to_add = true; } else { *k = -1; } } } // Quick continue of no elements to set/add if (!elements_to_add) return; // Get the nz locations in the argument corresponding to the inputs r_ind.resize(el_output.size()); copy(el_output.begin(), el_output.end(), r_ind.begin()); res[0].sparsity().get_nz(r_ind); // Enlarge the sparsity pattern of the arguments if not all assignments fit for (vector<int>::iterator k=r_nz.begin(); k!=r_nz.end(); ++k) { if (*k>=0 && nz[*k]>=0 && r_ind[nz[*k]]<0) { // Create a new pattern which includes both the the previous seed // and the addition/assignment Sparsity sp = res[0].sparsity().unite(osp); res[0] = res[0]->getProject(sp); // Recalculate the nz locations in the arguments corresponding to the inputs copy(el_output.begin(), el_output.end(), r_ind.begin()); res[0].sparsity().get_nz(r_ind); break; } } // Have r_nz point to locations in the result instead of the output for (vector<int>::iterator k=r_nz.begin(); k!=r_nz.end(); ++k) { if (*k>=0) { *k = r_ind[nz[*k]]; } } // Add to the element to the sensitivity, if any res[0] = arg[1]->getAddNonzeros(res[0], r_nz); }
void SetNonzeros<Add>::evalAdj(const std::vector<std::vector<MX> >& aseed, std::vector<std::vector<MX> >& asens) { // Get all the nonzeros vector<int> nz = all(); // Number of derivative directions int nadj = aseed.size(); // Output sparsity const Sparsity &osp = sparsity(); const int* orow = osp.row(); vector<int> ocol = osp.get_col(); // Input sparsity (first input same as output) const Sparsity &isp = dep(1).sparsity(); const int* irow = isp.row(); vector<int> icol = isp.get_col(); // We next need to resort the assignment vector by outputs instead of inputs // Start by counting the number of output nonzeros corresponding to each input nonzero vector<int> onz_count(osp.nnz()+2, 0); for (vector<int>::const_iterator it=nz.begin(); it!=nz.end(); ++it) { onz_count[*it+2]++; } // Cumsum to get index offset for output nonzero for (int i=0; i<onz_count.size()-1; ++i) { onz_count[i+1] += onz_count[i]; } // Get the order of assignments vector<int> nz_order(nz.size()); for (int k=0; k<nz.size(); ++k) { // Save the new index nz_order[onz_count[1+nz[k]]++] = k; } // Find out which elements are being set vector<int>& with_duplicates = onz_count; // Reuse memory onz_count.resize(nz.size()); for (int k=0; k<nz.size(); ++k) { // Get output nonzero int onz_k = nz[nz_order[k]]; // Get element (note: may contain duplicates) if (onz_k>=0) { with_duplicates[k] = ocol[onz_k]*osp.size1() + orow[onz_k]; } else { with_duplicates[k] = -1; } } // Get all output elements (this time without duplicates) vector<int> el_output; osp.find(el_output); // Sparsity pattern being formed and corresponding nonzero mapping vector<int> r_colind, r_row, r_nz, r_ind; for (int d=0; d<nadj; ++d) { // Get the matching nonzeros r_ind.resize(el_output.size()); copy(el_output.begin(), el_output.end(), r_ind.begin()); aseed[d][0].sparsity().get_nz(r_ind); // Sparsity pattern for the result r_colind.resize(isp.size2()+1); // Col count fill(r_colind.begin(), r_colind.end(), 0); r_row.clear(); // Perform the assignments r_nz.clear(); for (int k=0; k<nz.size(); ++k) { // Get the corresponding nonzero for the input int el = nz[k]; // Skip if zero assignment if (el==-1) continue; // Get the corresponding nonzero in the argument int el_arg = r_ind[el]; // Skip if no argument if (el_arg==-1) continue; // Save the assignment r_nz.push_back(el_arg); // Get the corresponding element int i=icol[k], j=irow[k]; // Add to sparsity pattern r_row.push_back(j); r_colind[1+i]++; } // col count -> col offset for (int i=1; i<r_colind.size(); ++i) r_colind[i] += r_colind[i-1]; // If anything to set/add if (!r_nz.empty()) { // Create a sparsity pattern from vectors Sparsity f_sp(isp.size1(), isp.size2(), r_colind, r_row); asens[d][1] += aseed[d][0]->getGetNonzeros(f_sp, r_nz); if (!Add) { asens[d][0] += MX::zeros(f_sp)->getSetNonzeros(aseed[d][0], r_nz); } else { asens[d][0] += aseed[d][0]; } } else { asens[d][0] += aseed[d][0]; } } }
std::vector<MX> SymbolicMX::partial(const std::vector<MX>& x){ return std::vector<MX>(1,MX::eye(sparsity().numel())); }
/** \brief Get required length of w field */ virtual size_t sz_w() const { return sparsity().size1();}
const std::vector<int>& SparseStorage<DataType>::row() const { return sparsity().row(); }